推荐参考博客:动态规划基础篇之最长公共子序列问题 - CSDN博客 https://blog.csdn.net/lz161530245/article/details/76943991
个人觉得上面的博客写的真的很好,我觉得我也要简单的写一写思路来加深一下理解,加深一下印象。
如果从前往后推,假设两个字符串str1,str2,长度分别是x,y,我们想求他们的最长公共子序列。
如果我们想知道dp[i][j](str1的前i个字符和str2的前j个字符的最长公共子序列长度)
如果str1[i]==str2[j],那么最长公共子序列的最后一个元素一定为str1[i],dp[i][j]=dp[i-1][j-1];
如果str1[i]!=str2[j],假设最长公共子序列最后一个元素为t,那么分三种情况
若t==str1[i]:那么str2[j]就可以排除在外了,即dp[i][j]=dp[i][j-1];
若t==str2[j]:那么str1[i]就可以排除在外了,即dp[i][j]=dp[i-1][j];
若t!=str1[i]&&t!=str2[j]:那么str1[i],str2[j]都排除在外,即dp[i][j]=dp[i-1][j-1];但是实际上出现这种情况的时候,
dp[i-1][j-1]=dp[i-1][j]=dp[i][j-1];因为我们在str1后面加一个str1[i]或者在str2后面加一个str2[j],它的最长公共子序列是不变的,长度当然也不变。所以我们得出结论:若str1[i]!=str[j],dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
期间还可以用path记录路径,输出最长公共子序列
例题:http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1006
#include<iostream> #include<cstring> #include<algorithm> #include<queue> #include<map> #include<stack> #include<cmath> #include<vector> #include<fstream> #include<set> #include<cstdio> #include<string> #include<deque> using namespace std; #define eps 1e-8 #define ll long long #define INF 0x3f3f3f3f int dp[1005][1005]; char str1[1005],str2[1005]; int path[1005][1005]; int len1,len2; void printf_lcs(int a,int b) { if(!a||!b)//终止条件,其中一个字符串已经走完了 return; if(path[a][b]==1)//往对角线方向走 { printf_lcs(a-1,b-1); cout<<str1[a-1]; } else if(path[a][b]==2)//往上走 printf_lcs(a-1,b); else printf_lcs(a,b-1);//往左走 } int main() { cin>>str1; cin>>str2; len1=strlen(str1); len2=strlen(str2); memset(dp,0,sizeof(dp)); memset(path,0,sizeof(path)); for(int i=1;i<=len1;i++) { for(int j=1;j<=len2;j++) { if(str1[i-1]==str2[j-1]) { dp[i][j]=dp[i-1][j-1]+1; path[i][j]=1;//1表示str1[i-1],str2[j-1]两个字符相同 ,可以输出 ,往对角线方向走 } else //最后两个字符不同时 { if(dp[i-1][j]>=dp[i][j-1]) { dp[i][j]=dp[i-1][j]; path[i][j]=2;//2表示在我 推荐的博客 的那张图里往上走 } else { dp[i][j]=dp[i][j-1]; path[i][j]=3;//3表示在我 推荐的博客 的那张图里往左走 } } } } printf_lcs(len1,len2); cout<<endl; return 0; }