上篇总结了最长公共子序列用动态规划求解的问题,由此也引出了最长公共子串使用动态规划思想求解的问题。
再次辨析下两者的关系,
最长公共子序列 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 }