题目链接:http://lightoj.com/volume_showproblem.php?problem=1013
#include <cstdio> #include <cstring> #include <iostream> #include <cmath> #include <algorithm> #include <queue> #include <vector> using namespace std; const int maxn = 35; const int INF = 0x3f3f3f; int main() { //freopen("E:\acm\input.txt","r",stdin); int T; cin>>T; for(int t=1;t<=T;t++){ long long dp1[maxn][maxn],dp2[maxn][maxn]; /** dp1[i][j] = (i+j) - num(s1中前i个和s2中前j个的最长公共子序列)。 dp2[i][j]是包含s1中前i个和s2中前j个字母的最短字符串的个数。 **/ char s1[maxn],s2[maxn]; scanf("%s %s",s1+1,s2+1); int Len1 = strlen(s1+1), Len2 = strlen(s2+1); for(int i=0;i<=Len1;i++) dp1[i][0] = i, dp2[i][0] = 1; for(int i=0;i<=Len2;i++) dp1[0][i] = i, dp2[0][i] = 1; for(int i=1;i<=Len1;i++) for(int j=1;j<=Len2;j++){ if(s1[i] == s2[j]){ dp1[i][j] = dp1[i-1][j-1] + 1; dp2[i][j] = dp2[i-1][j-1]; //这个时候直接把s1[i](s2[j])放在合成串s后面,所以加一; } else{ if(dp1[i-1][j] == dp1[i][j-1]){ dp1[i][j] = dp1[i-1][j] + 1; dp2[i][j] = dp2[i-1][j] + dp2[i][j-1]; /**这个地方最难理解,dp1[i-1][j] == dp1[i][j-1] 得出s1[i-1] != s2[j] ,s1[i] != s2[j-1]. dp2[i-1][j] 可以理解为把s1[i]放在合成串s的最后的方法数,dp2[i][j-1]可以理解为把s2[j]放在合成串s最后的方法数。 加起来就的到总共的组合数 **/ } else if(dp1[i-1][j] > dp1[i][j-1]){ //1.说明s1[i]能与s2[j-1]或者j-1之前某个组合在一起,而s2[j]不能。 dp1[i][j] = dp1[i][j-1] + 1; //取小的; 并添加了一个字母s2[j]; dp2[i][j] = dp2[i][j-1]; //由1.这句知道:s2[j]只能添加在合成串s的最后。 } else{ //此处分析同上。 dp1[i][j] = dp1[i-1][j] + 1; //取小的; 并添加了一个字母s1[i]; dp2[i][j] = dp2[i-1][j]; } } } printf("Case %d: %lld %lld ",t,dp1[Len1][Len2],dp2[Len1][Len2]); } } //总结下,这个dp1其实就是简单的LCS的变形,而dp2就是关键,这是参考别人的方法,只是觉得很精妙就学习下。