問題:例如:x=,y=,那麼,二者的最長公共子串行是,長度為4。
我們首先需要搞清楚以下兩個概念:
最長公共子串行 vs 最長公共子串:
找兩個字串的最長公共子串,這個子串要求在原字串中是連續的。而最長公共子串行則並不要求連續。
上述問題中的最長公共子串行與最長公共子串是一樣的。
但是再舉例x=,y=,二者的最長公共子串行是,而二者的最長公共子串是。
求解思路:
1.分析最優解的結構特徵:
設zk=是xm=和yn=的最長公共子串行。
則可以得到:
若xm=yn=zk,那麼zk-1=是xm-1=和yn-1=的最長公共子串行;
若xm≠yn,xm≠zk,則去除xm後,zk=仍然是xm-1=和yn=的最長公共子串行;
若xm≠yn,yn≠zk,則去除yn後,zk=仍然是xm=和yn-1=的最長公共子串行;
2.建立最優值的遞迴式
資料結構選擇:
用c[i][j]表示xi和yj的最長公共子串行長度(這一步很關鍵,越到右下角,值會越來越大,我們最後只需要選取右下角的值就可以確定最長公共子串行的長度)
討論:若xi=yj=zk,那麼c[i][j] = c[i-1][j-1] + 1;
若xi≠yj,xi≠zk,那麼xi需要進一步縮小乙個長度進行匹配,即去除xi不影響整體的最長子序列變化,c[i][j] = c[i-1][j] ;
若xi≠yj,yj≠zk,那麼yj需要進一步縮小乙個長度進行匹配,即去除yj不影響整體的最長子序列變化,c[i][j] = c[i][j-1] ;
結束條件,若i=0或者j=0,則c[i][j]=0。
所以,在xi≠yj的情況下,有兩種情況,c[i][j]必等於兩種情況下的最大值,即c[i][j] = max(c[i-1][j], c[i][j-1])
3.自底向上計算最優值,並記錄最優值與最優策略
我們由上面可以知道,c[0][j]=0或者c[i][0]=0.
先使i = 1,則求x1與逐一比較。如圖,x1≠y1,執行c[1][1] = max(c[0][1], c[1][0]) =0,接著,x1=y2,則執行,c[1][2] = c[0][1] + 1=1 ,接著,x1≠y3,則執行c[1][3] = max(c[0][3], c[1][2]) =1
,......這樣就求出了x1與yn的最長公共子串行長度;
然後,i = 2,則建立在x1比較的基礎上就可以求出與的最長公共子串行長度;
然後,i = 3,建立在的基礎上,則可以求出與的最長公共子串行長度;
......
然後,i = m,建立在的基礎上,則可以求出與的最長公共子串行長度;
4.構造最優解
在知道了最長公共子串行的長度之後,我們還需要知道最長公共子串行中都是哪些元素。我們在c[i][j]陣列的右下角能夠得到最長公共子串行的長度,那麼,我們可以反向推出這個元素分別是什麼。
由上述,我們可以得到:
若xi=yj=zk,c[i][j] = c[i-1][j-1] + 1;
若xi≠yj,xi≠zk,c[i][j] = c[i-1][j] ;
若xi≠yj,yj≠zk,c[i][j] = c[i][j-1] ;
所以,c[i][j]由上述三個等式中的乙個得到,那麼我們只需要記錄下c[i][j]是從三個等式中哪乙個得到的,那麼對應的元素我們就知道了。
這樣,我們就必須在借助乙個陣列就行儲存了,我們建立新的陣列b[i][j]來記錄是從哪個等式中得到,即構造出下列結構。
若xi=yj=zk,c[i][j] = c[i-1][j-1] + 1,則b[i][j] = 1,那麼我們就可以取出xi或者yj作為最長公共子串行中的元素;
若xi≠yj,yj≠zk,c[i][j] = c[i][j-1],則b[i][j] = 2,那麼我們就可以去追蹤c[i][j-1];
若xi≠yj,xi≠zk,c[i][j] = c[i-1][j] ,則b[i][j] = 3,那麼我們就可以去追蹤c[i-1][j];
追蹤到i=0或者j=0,停止,如下圖則是根據c[i][j]取值的**將b[i][j]陣列補充完整
下圖為兩個序列的最終比較情況:我們由上述構造b[i][j]的方法得知,若 b[i][j]= 2,那麼我們去追蹤c[i][j-1],若b[i][j]= 3,那麼我們去追蹤c[i-1][j],換言之,就是b[i][j]= 2
,就往左找,b[i][j]= 3,就往右找,若b[i][j]= 1,就可以輸出此時的xi或者yj
上述的思路理解清楚了,我們就可以上**了,**如下:
1 #include 2 #include 3using
namespace
std;
4const
int n=1024;5
intc[n][n],b[n][n];
6char
s1[n],s2[n];
7int
len1,len2;
8void
lcs()916
else
21else25}
26}27}
28}2930
void lcs_print(int i, int
j)31
35if(b[i][j] == 1)39
else
if(b[i][j] == 2)42
else45}
4647
intmain()
4858
for(int j = 0; j <= len2; j++)
61lcs();
62 cout << "
s1與s2的最長公共子串行的長度是:
"<< c[len1][len2] <
63 cout << "
s1與s2的最長公共子串行是:";
64lcs_print(len1,len2);
65return0;
66 }
ACM 最長公共子串行 動態規劃求解
最長公共子串行問題 給你兩個字串a m b n 求出他們的公共子字串的長度 子字串不需要再原字串中連續 這個問題的一般想法是 1.找出a m 中的每個子字串 2.看其是否也能存在在b n 中 3.找出公共子字串中最大的乙個。然而這個方法對於計算機來說實在是過於複雜,比較好的方法是使用動態規劃的方法 ...
最長公共子串行 動態規劃
經常會遇到複雜問題不能簡單地分解成幾個子問題,而會分解出一系列的子問題。簡單地採用把大問題分解成子問題,並綜合子問題的解匯出大問題的解的方法,問題求解耗時會按問題規模呈冪級數增加。為了節約重複求相同子問題的時間,引入乙個陣列,不管它們是否對最終解有用,把所有子問題的解存於該陣列中,這就是動態規劃法所...
最長公共子串行 動態規劃
關於用動態規劃法求兩個序列的最長公共子串行問題的相關知識見 王曉東 計算機演算法設計與分析 第三章。注意,這裡所指的最長公共子串行是可以不相鄰的,與平常所說的最長公共子串 相鄰的 不一樣。直接上 lcs.h ifndef lcs h define lcs h class lcstring endif...