题目描述:
给定两个字符串s1s2……sn和t1t2……tn。求出这两个字符串最长的公共子序列的长度。字符串s1s2……sn的子序列指可以表示为si1si2……sim(i1<i2<……<im)的序列。(n>=1,m<=1000)
输入:
n=4
m=4
s="abcd"
t="becd"
输出:
3
分析:
阶段就是n=0,1,2,3,4……时,m=0,1,2,3……1000时;状态就是对应字符相等还是不相等。
看该题是否符合,每个阶段的最优状态可以从之前的某个阶段的某个或某些状态得到而不管之前的状态是如何得到的(最优子结构和无后效性)。
用输入输出的栗子证一下:
当n=4,m=4时,S3为‘d’,T3为‘d’,S3==T3,则此阶段对应的最长公共子序列(LCS)为n=3,m=3时的LCS加1。
当n=2,m=2时,S1为‘b‘,T1为‘e‘,S1!=T3,则此阶段对应的LCS为,n=2,m=1阶段的LCS与n=1,m=2阶段的LCS的最大值。
记忆化搜索代码:
#include <iostream> #include <string> #include <string.h> #define MAX_N 1001 #define MAX_M 1001 using namespace std; int n,m; string s,t; int dp[MAX_N][MAX_M]; int rec(int ni,int mi){ if(dp[ni][mi]>=0){ return dp[ni][mi]; } int res; //当s和t的字符串长度为1时,它们参考的前一个LCS设为0 if(ni==0||mi==0){ res=0; } else if((ni>0&&ni<=n)&&(mi>0&&mi<=m)){ if(s[ni-1]==t[mi-1]){ res=rec(ni-1,mi-1)+1; } else if(s[ni-1]!=t[mi-1]){ res=max(rec(ni-1,mi),rec(ni,mi-1)); } } dp[ni][mi]=res; return res; } int main() { cin>>n>>m; cin>>s>>t; memset(dp,-1,sizeof(dp)); cout<<rec(n,m)<<endl; for(int i=0;i<=n;i++){ for(int j=0;j<=m;j++){ cout<<dp[i][j]; if(j<m){ cout<<‘ ‘; } else if(j==m){ cout<<endl; } } } return 0; }
根据记忆化搜索推到出递推公式,本题比较简单也可以直接写出来:
当m或n等于0,dp[n][0]=0,dp[0][m]=0;
当Si与Ti相等时,dp[n][m]=dp[n-1][m-1]+1;
当Si与Ti不相等时,dp[n][m]=max(dp[n-1][m],dp[n][m-1]);
举个不相等的栗子:
"abcdef"和"defihg",‘f‘和‘g‘不相等,则考虑"abcdef"和"defih"其LCS为3。
动态规划的代码:
#include <iostream> #include <string> #define MAX_N 1001 #define MAX_M 1001 using namespace std; int dp[MAX_N][MAX_M]; int main() { int n,m; string s,t; cin>>n>>m; cin>>s>>t; //递推公式中的n用i+1表示,m用j+1表示 for(int i=0;i<n;i++){ for(int j=0;j<m;j++){ if(s[i]==t[j]){ dp[i+1][j+1]=dp[i][j]+1; } else if(s[i]!=t[j]){ dp[i+1][j+1]=max(dp[i][j+1],dp[i+1][j]); } } } cout<<dp[n][m]<<endl; return 0; }
时间: 2024-10-13 04:19:27