最长公共子序列
注意:
(1)定义子问题:
dp[i][j]字符串A的前i个和字符串B的前j个拥有的最长公共子序列的长度
(2)子问题的递推关系:
(3)边界
当其中一个子序列为空时,公共子序列个数为0。所以dp的边界如下图所示
字符串a/字符串b | A | B | B | D | A | |
---|---|---|---|---|---|---|
0 | 0 | 0 | 0 | 0 | 0 | |
B | 0 | |||||
D | 0 | |||||
C | 0 | |||||
A | 0 |
本次代码中对边界的赋值也比较巧妙,直接memset(dp,0,sizeof(dp))
4.1 如果单纯求最长公共子序列的个数:
#include <bits/stdc++.h>
using namespace std;
/*
dp[i][j]字符串A的前i个和字符串B的前j个拥有的最长公共子序列的长度
*/
int main()
{
int dp[101][101]; //字符串长度最长100
string a,b;
cin>>a;
cin>>b;
/*----------边界----------*/
memset(dp,0,sizeof(dp));
/*----------递推-----------*/
for(int i=1;i<=a.size();i++){
for(int j=1;j<=b.size();j++){
if(a[i]==b[j])
dp[i][j]=dp[i-1][j-1]+1;
else
dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
}
}
cout<<"The amount of the same subset is:"<<dp[a.size()][b.size()]<<endl;;
return 0;
}
4.2 如果还要输出最长公共子序列,那么还要加上回溯的操作
这就需要我们区分当a[i]!=b[j]时,到底选的哪一个。因为回溯的时候涉及到了字符串a和字符串b各自指针的移动,到底移动a的字符串还是b的就得看选的哪一种情况。
这里借助和dp一样大小的数组来记录。
#include <bits/stdc++.h>
using namespace std;
/*
dp[i][j]字符串A的前i个和字符串B的前j个拥有的最长公共子序列的长度
*/
int dp[101][101]; //字符串长度最长100
int note[101][101];
string a,b;
void printIt(int i,int j){
if(i==0||j==0) return; //别忘了递归出口
if(note[i][j]==1){
printIt(i-1,j-1);
cout<<a[i-1]; //先找到的后输出
}else if(note[i][j]==2){
printIt(i-1,j);
}else
printIt(i,j-1);
}
int main()
{
cin>>a;
cin>>b;
/*----------边界----------*/
memset(dp,0,sizeof(dp));
memset(note,0,sizeof(note));
/*----------递推-----------*/
for(int i=1;i<=a.size();i++){
for(int j=1;j<=b.size();j++){
if(a[i]==b[j]){
dp[i][j]=dp[i-1][j-1]+1;
note[i][j]=1; //做个标记
}
else{
if(dp[i-1][j]>dp[i][j-1]){
dp[i][j]=dp[i-1][j];
note[i][j]=2; //做个标记
}else{
dp[i][j]=dp[i][j-1];
note[i][j]=3; //做个标记
}
}
}
}
cout<<"The amount of the same subset is:"<<dp[a.size()][b.size()]<<endl;
/*--------输出最长公共子序列---------*/
printIt(a.size(),b.size());
return 0;
}