上篇總結了最長公共子序列用動態規划求解的問題,由此也引出了最長公共子串使用動態規划思想求解的問題。
再次辨析下兩者的關系,
最長公共子序列 VS 最長公共子串:
找兩個字符串的最長公共子串,這個子串要求在原字符串中是連續的。而最長公共子序列則並不要求連續。
其實話句話說最長公共子序列中包含着最長公共子串。
其實最長公共子串的算法求解思想與最長公共子序列的思路基本類似。
1.在最長公共子序列中,核心的遞推式如下:
若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] ;
2.但是在最長公共子串中,要求原字符串是連續的,那么就需要對最長公共子序列的核心遞推式進行修改了。
在最長公共子串中,我們同樣建立c[i][j]來保存兩個序列的字符異同情況,由於最長公共子串要求串必須是連續的,所以,在xi≠yj時,我們就需要將c[i][j]記為0,若相同則記為1。
根據下圖,在對角線上連續為1的表示兩個字符串是相同的,而且,對角線上連續的1越多,表示兩個字符串的最長公共子串越長,我們能夠發現,下圖的最長為三個1,所以其長度為3,最長公共子串為DCD。
3.上述我們還需要針對對角線上連續1的個數進行匯總,這樣才能求得最長公共子串的長度。
我們可以針對上面的遞推式進行修改,我們知道,要是xi和yj是屬於一個公共子串的最后一個字符(假設其長度大於等於2),那么一定有xi==yj&&xi-1 = =yj-1。
所以,我們在求c[i][j]時,要是X[i]==Y[j],c[i][j] = c[i-1][j-1] + 1,無論 xi==yj是否相等。
那么我們就可以對上圖進行更新了,更新如下:
上圖我們只要遍歷數組就能得到最長公共子串的長度。
4.上述的思維,我們可以求出兩個字符串的最長公共子串的長度,但是,我們還需要輸出最長的公共子串中的元素是是什么,這就要求我們用一個額外的空間進行記錄。
因為最長公共子串肯定為兩個字符串的子集,所以其長度必然小於等於兩個字符串的長度。
我們可以借助兩個變量對最大長度以及最大長度的停止下標進行記錄,分別借助maxLen,flag;
在循環中以此將數組中最大的數賦值給maxLen,並用flag來記錄此時的行/列字符串的下標,后續可以進行遍歷。
上述已經將整個思路理清,那么下面放代碼:
1 #include <iostream> 2 #include <cstring> 3 using namespace std; 4 const int N=1024; 5 int c[N][N]; 6 int maxLen=0,flag=0; 7 char s1[N],s2[N]; 8 int len1,len2; 9 void LCSs() 10 { 11 for(int i = 1; i <= len1; i++){ 12 for(int j = 1; j <= len2; j++){ 13 if(s1[i-1] == s2[j-1]){ //注:此處的s1與s2序列是從s1[0]與s2[0]開始的 14 c[i][j] = c[i-1][j-1] + 1; 15 if(c[i][j] > maxLen){ 16 maxLen = c[i][j]; 17 flag = j; //注:此處的flag在s2中尋找應該從s2[flag-maxLen]-s2[flag-1]尋找 18 } 19 } 20 else{ 21 c[i][j] = 0; 22 } 23 24 } 25 } 26 } 27 28 void LCSs_PRINT(int flag,int maxLen,int len2 ) 29 { 30 if(flag==0 || maxLen==0){ 31 return; 32 } 33 for(int i = flag-maxLen; i < flag; i++){ 34 cout << s2[i]; 35 } 36 } 37 38 int main() 39 { 40 cout << "請輸入X字符串" << endl; 41 cin >> s1; 42 cout << "請輸入Y字符串" << endl; 43 cin >> s2; 44 len1 = strlen(s1); 45 len2 = strlen(s2); 46 for(int i = 0; i <= len1; i++){ 47 c[i][0] = 0; 48 } 49 for(int j = 0; j <= len2; j++){ 50 c[0][j] = 0; 51 } 52 LCSs(); 53 cout << "s1與s2的最長公共子序列的長度是:" << maxLen <<endl; 54 cout << "s1與s2的最長公共子序列是:"; 55 LCSs_PRINT(flag,maxLen,len2); 56 return 0; 57 }