学了一下最长公共子串,它是属于dp里面的 dp=max{(i,j-1),(i-1,j),(i-1,j-1)+d}问题,不得不说,规划方向确实厉害,当然这只适用于两个字符串匹配的问题,n个字符串的话,我查了一下,要用后缀数组,这个以后再来弄吧。
比较难的是记录公共字串,在DP的时候记录好搜索的方向,就可以询步骤得到公共串。尤其1503这题呢,除了把公共部分打印出来,还要把非公共部分打印出来。
#include <iostream> #include <cstdio> #include <cstring> using namespace std; char a[105] , b[105]; int dp[105][105]; int rec[105][105]; int dir[105][105]; int LCS(int r,int c) { if (dp[r][c]>=0) return dp[r][c]; //用DP+记忆化搜索写的LCS if (a[r-1]==b[c-1]){ dp[r][c]=LCS(r-1,c-1)+1; dir[r][c]=1; } else { int dp1=LCS(r-1,c); int dp2=LCS(r,c-1); if (dp1>dp2){ dp[r][c]=dp1; dir[r][c]=2; //用dir记录规划的方向 } else { dp[r][c]=dp2; dir[r][c]=3; } } return dp[r][c]; } void output(int r,int c)//已经用dir记录了规划方向,这里用递归将字符串还原出来 { if (r==0&&c==0) return; //一开始这里犯糊涂了,写成或,要知道只有一个为0的话,还要把其他非公共部分打印出来,还得继续下去。 // cout<<"dir"<<" "<<r<<" "<<c<<" "<<dir[r][c]<<endl; if (dir[r][c]==1){ output(r-1,c-1); printf("%c",a[r-1]); } if (dir[r][c]==2){ //此时a[r-1]相当于被抛弃了,所以可以打印了,下面同理 output(r-1,c); printf("%c",a[r-1]); } if (dir[r][c]==3){ output(r,c-1); printf("%c",b[c-1]); } } int main() { while (scanf("%s%s",a,b)!=EOF) { int len1=strlen(a); int len2=strlen(b); int i,j; for (i=0;i<=len1;i++){ for (j=0;j<=len2;j++){ dp[i][j]=-1; if (i==0||j==0) dp[i][j]=0; if (i==0) dir[i][j]=3;//为了让output把所有非公共部分也打印完,得预先把这些设置好 if (j==0) dir[i][j]=2; } } int ans=LCS(len1,len2); output(len1,len2); //printf("%d",ans); putchar(' '); } }