字符串最长公共子序列python_求解两个字符串的最长公共子序列
一,問題描述
給定兩個(gè)字符串,求解這兩個(gè)字符串的最長公共子序列(Longest Common Sequence)。比如字符串1:BDCABA;字符串2:ABCBDAB
則這兩個(gè)字符串的最長公共子序列長度為4,最長公共子序列是:BCBA
二,算法求解
這是一個(gè)動態(tài)規(guī)劃的題目。對于可用動態(tài)規(guī)劃求解的問題,一般有兩個(gè)特征:①最優(yōu)子結(jié)構(gòu);②重疊子問題
①最優(yōu)子結(jié)構(gòu)
設(shè) X=(x1,x2,.....xn) 和 Y={y1,y2,.....ym} 是兩個(gè)序列,將 X 和 Y 的最長公共子序列記為LCS(X,Y)
找出LCS(X,Y)就是一個(gè)最優(yōu)化問題。因?yàn)?#xff0c;我們需要找到X 和 Y中最長的那個(gè)公共子序列。而要找X 和 Y的LCS,首先考慮X的最后一個(gè)元素和Y的最后一個(gè)元素。
1)如果 xn=ym,即X的最后一個(gè)元素與Y的最后一個(gè)元素相同,這說明該元素一定位于公共子序列中。因此,現(xiàn)在只需要找:LCS(Xn-1,Ym-1)
LCS(Xn-1,Ym-1)就是原問題的一個(gè)子問題。為什么叫子問題?因?yàn)樗囊?guī)模比原問題小。(小一個(gè)元素也是小嘛....)
為什么是最優(yōu)的子問題?因?yàn)槲覀円业氖荴n-1和 Ym-1?的最長公共子序列啊。。。最長的!!!換句話說,就是最優(yōu)的那個(gè)。(這里的最優(yōu)就是最長的意思)
2)如果xn != ym,這下要麻煩一點(diǎn),因?yàn)樗a(chǎn)生了兩個(gè)子問題:LCS(Xn-1,Ym) 和 LCS(Xn,Ym-1)
因?yàn)樾蛄蠿 和 序列Y 的最后一個(gè)元素不相等嘛,那說明最后一個(gè)元素不可能是最長公共子序列中的元素嘛。(都不相等了,怎么公共嘛)。
LCS(Xn-1,Ym)表示:最長公共序列可以在(x1,x2,....x(n-1)) 和 (y1,y2,...yn)中找。
LCS(Xn,Ym-1)表示:最長公共序列可以在(x1,x2,....xn) 和 (y1,y2,...y(n-1))中找。
求解上面兩個(gè)子問題,得到的公共子序列誰最長,那誰就是 LCS(X,Y)。用數(shù)學(xué)表示就是:
LCS=max{LCS(Xn-1,Ym),LCS(Xn,Ym-1)}
由于條件 1)? 和? 2)? 考慮到了所有可能的情況。因此,我們成功地把原問題 轉(zhuǎn)化 成了 三個(gè)規(guī)模更小的子問題。
②重疊子問題
重疊子問題是啥?就是說原問題 轉(zhuǎn)化 成子問題后,? 子問題中有相同的問題。咦?我怎么沒有發(fā)現(xiàn)上面的三個(gè)子問題中有相同的啊????
OK,來看看,原問題是:LCS(X,Y)。子問題有 ?LCS(Xn-1,Ym-1)??? ?LCS(Xn-1,Ym) ?? ?LCS(Xn,Ym-1)
初一看,這三個(gè)子問題是不重疊的。可本質(zhì)上它們是重疊的,因?yàn)樗鼈冎恢丿B了一大部分。舉例:
第二個(gè)子問題:LCS(Xn-1,Ym) 就包含了:問題?LCS(Xn-1,Ym-1),為什么?
因?yàn)?#xff0c;當(dāng)Xn-1?和 Ym?的最后一個(gè)元素不相同時(shí),我們又需要將LCS(Xn-1,Ym)進(jìn)行分解:分解成:LCS(Xn-1,Ym-1)?和 LCS(Xn-2,Ym)
也就是說:在子問題的繼續(xù)分解中,有些問題是重疊的。
由于像LCS這樣的問題,它具有重疊子問題的性質(zhì),因此:用遞歸來求解就太不劃算了。因?yàn)椴捎眠f歸,它重復(fù)地求解了子問題啊。而且注意哦,所有子問題加起來的個(gè)數(shù) 可是指數(shù)級的哦。。。。
這篇文章中就演示了一個(gè)遞歸求解重疊子問題的示例。
那么問題來了,你說用遞歸求解,有指數(shù)級個(gè)子問題,故時(shí)間復(fù)雜度是指數(shù)級。這指數(shù)級個(gè)子問題,難道用了動態(tài)規(guī)劃,就變成多項(xiàng)式時(shí)間了??
呵呵噠。。。。
關(guān)鍵是采用動態(tài)規(guī)劃時(shí),并不需要去一 一 計(jì)算那些重疊了的子問題。或者說:用了動態(tài)規(guī)劃之后,有些子問題 是通過 “查表“ 直接得到的,而不是重新又計(jì)算一遍得到的。廢話少說:舉個(gè)例子吧!比如求Fib數(shù)列。關(guān)于Fib數(shù)列,可參考:
求fib(5),分解成了兩個(gè)子問題:fib(4) 和 fib(3),求解fib(4) 和 fib(3)時(shí),又分解了一系列的小問題....
從圖中可以看出:根的左右子樹:fib(4) 和 fib(3)下,是有很多重疊的!!!比如,對于 fib(2),它就一共出現(xiàn)了三次。如果用遞歸來求解,fib(2)就會被計(jì)算三次,而用DP(Dynamic Programming)動態(tài)規(guī)劃,則fib(2)只會計(jì)算一次,其他兩次則是通過”查表“直接求得。而且,更關(guān)鍵的是:查找求得該問題的解之后,就不需要再繼續(xù)去分解該問題了。而對于遞歸,是不斷地將問題分解,直到分解為 基準(zhǔn)問題(fib(1) 或者 fib(0))
說了這么多,還是要寫下最長公共子序列的遞歸式才完整。借用網(wǎng)友的一張圖吧:)
c[i,j]表示:(x1,x2....xi) 和 (y1,y2...yj) 的最長公共子序列的長度。(是長度哦,就是一個(gè)整數(shù)嘛)。公式的具體解釋可參考《算法導(dǎo)論》動態(tài)規(guī)劃章節(jié)
三,LCS python實(shí)現(xiàn)
1 defLCS(str1, str2):2 c = [[0 for i in range(len(str2)+1)] for j in range(len(str1)+1)]3 for i in range(1, len(str1)+1):4 for j in range(1, len(str2)+1):5 if str1[i-1] == str2[j-1]:6 c[i][j] = c[i-1][j-1] + 1
7 else:8 c[i][j] = max(c[i][j-1], c[i-1][j])9 printc10 return c[-1][-1]11
12 if __name__ == '__main__':13 str1 = 'BDCABA'
14 str2 = 'ABCBDAB'
15 print LCS(str1, str2)
感覺整個(gè)代碼就是直接根據(jù)上面的那個(gè)遞歸表達(dá)式寫的。
①第1行定義一個(gè)數(shù)組來保存最長公共子序列的長度
②第7,8行,就是遞歸表達(dá)式的程序表示。就是:? c[i,j] = max{c[i][j-1], c[i-1][j]}
③第10行返回最終結(jié)果。
總結(jié)
以上是生活随笔為你收集整理的字符串最长公共子序列python_求解两个字符串的最长公共子序列的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java_基础阶段笔记总结汇总
- 下一篇: 贪吃蛇python小白_面向 pytho