最长公共子序列 (LCS)
继上次讲完求LCS长度后,来更新一篇具体求LCS的求法。
in:
abcicba
abdkscab
out:
abca
我们已经知道lcs中的状态转移方程为:
for(int i=0;i<=a;i++) { for(int j=0;j<=b;j++) { if(i==0||j==0) { dp[i][j]=0; continue; } if(s1[i-1]==s2[j-1]) dp[i][j]=dp[i-1][j-1]+1; else dp[i][j]=max(dp[i-1][j],dp[i][j-1]); } }
得出,在进行dp时,dp[i][j]的状态由三种可能的状态转移过来,第一种状态表示第一个序列的前i-1个字符与第二个序列前j-1个字符的公共子序列加上最后一个字符的录入状态,如果最后的一个字符相等则录入状态为1;第二种状态表示不录入第二个序列的第j个字符时的最长公共子序列;第三种状态表示不录入第一个序列的第i个字符时的最长公共子序列。
那么有:
while(i!=0 && j!=0) { if(a[i-1] == b[j-1])//对应第一种情况 { c[z++] = a[--i];//将a[i-1]录入c中 j--; } else if(dp[i-1][j] < dp[i][j-1])//第二种 j--; else if(dp[i][j-1] <= dp[i-1][j])//第三种 i--; }
加上完整的路径打印为
#include<bits/stdc++.h> using namespace std; char a[1001], b[1001]; int dp[1001][1001], len1, len2; void lcs(int i, int j) { for(i=1; i<=len1; i++) { for(j=1; j<=len2; j++) { if(a[i-1] == b[j-1]) dp[i][j] = dp[i-1][j-1] + 1; else if(dp[i-1][j] > dp[i][j-1]) dp[i][j] = dp[i-1][j]; else dp[i][j] = dp[i][j-1]; } } } void getlcs() { int i, j, z = 0; char c[1001]; memset(c, 0, sizeof(c)); i = len1, j = len2; while(i!=0 && j!=0) { if(a[i-1] == b[j-1]) { c[z++] = a[--i]; j--; } else if(dp[i-1][j] < dp[i][j-1]) j--; else if(dp[i][j-1] <= dp[i-1][j]) i--; } for(i=z-1; i>=0; i--) printf("%c", c[i]); putchar('\n'); } int main() { while(~scanf(" %s", a)) { scanf(" %s", b); memset(dp, 0, sizeof(dp)); len1 = strlen(a); len2 = strlen(b); lcs(len1, len2); getlcs(); } return 0; }