1. 两者区别
约定:在本文中用 LCStr 表示最长公共子串(Longest Common Substring),LCSeq 表示最长公共子序列(Longest Common Subsequence)。
子串要求在原字符串中是连续的,而子序列则没有要求。例如:
字符串 s1=abcde,s2=ade,则 LCStr=de,LCSeq=ade。
2. 求最长公共子串(LCStr)
算法描述:构建如下图的矩阵dp[][],当s1[i] == s2[j] 的时候,dp[i][j]=1;最后矩阵中斜对角线上最长的“1”序列的长度,就是 LCStr 的长度。
但是求矩阵里斜线上的最长的“1”序列,仍然略显麻烦,我们进行如下优化:当要往矩阵中填“1”的时候,我们不直接填“1”,而是填“1”+左上角的那个数。如下图所示:
这样,我们只需求出矩阵里的最大数(注意:最大数可不一定在最右下角,别误解了上图),即是 LCStr 的长度。
要求出这个 LCStr,其他的不多说了,见代码中注释。
C++ code:
1 #include <iostream> 2 #include <string> 3 #include <cstdlib> // freopen 4 #include <cstring> // memset 5 using namespace std; 6 7 #define MAXN 2001 8 static int dp[MAXN][MAXN]; 9 10 string LCStr(const string &s1, const string &s2) 11 { 12 string result; 13 14 //s1纵向,s2横向 15 //len1行,len2列 16 int len1=s1.length(), len2=s2.length(); 17 memset(dp,0,sizeof(dp)); 18 19 //预先处理第一行第一列 20 for(int i=0; i<len2; ++i) 21 if(s1[0]==s2[i]) dp[0][i]=1; 22 for(int i=0; i<len1; ++i) 23 if(s1[i]==s2[0]) dp[i][0]=1; 24 25 for(int i=1; i<len1; ++i) 26 for(int j=1; j<len2; ++j) 27 if(s1[i]==s2[j]) dp[i][j]=dp[i-1][j-1]+1; //矩阵填充 28 29 //将第一行的最大值移到最右边 30 for(int i=1; i<len2; ++i) 31 if(dp[0][i]<dp[0][i-1]) dp[0][i]=dp[0][i-1]; 32 33 //从第二行开始,将每一行的最大值移到最右边 34 //最后边的数和上一行的最右边数比较大小,将大的下移 35 //到最后,右下角的数就是整个矩阵的最大值 36 for(int i=1; i<len1; ++i) 37 { 38 for(int j=1; j<len2; ++j) 39 if(dp[i][j]<dp[i][j-1]) dp[i][j]=dp[i][j-1]; 40 if(dp[i][len2-1]<dp[i-1][len2-1]) dp[i][len2-1]=dp[i-1][len2-1]; 41 } 42 cout<<"length of LCStr: "<<dp[len1-1][len2-1]<<endl; 43 44 int max = dp[len1-1][len2-1]; 45 int pos_x; 46 for(int i=0; i<len1; ++i) 47 for(int j=0; j<len2; ++j) 48 { 49 if(dp[i][j]==max) 50 { 51 pos_x=i; 52 j=len2; /// 53 i=len1; ///快速跳出循环 54 } 55 } 56 result=s1.substr(pos_x-max+1,max); 57 return result; 58 } 59 60 int main() 61 { 62 int t; 63 freopen("in.txt","r",stdin); 64 cin>>t; 65 cout<<"total tests: "<<t<<endl<<endl; 66 while(t--) 67 { 68 string a,b; 69 cin>>a>>b; 70 cout<<a<<endl<<b<<endl; 71 72 string res=LCStr(a,b); 73 cout<<"LCStr: "<<res<<endl<<endl; 74 } 75 return 0; 76 }