问题描述:
最长公共子序列。给定两个长度分别为M和N的字符串a和b,求既是a的子序列又是b的子序列的字符串长度最长是多少。
状态表示:
f[i][j]表示前缀字串a[1~i]与b[1~j]的“最长公共子序列”的长度。
阶段划分:
已经处理的前缀长度(两个字符串中的位置,即一个二维坐标系)
转移方程:
f[i,j]=max{f[i-1,j],fg[i,j-1]} a[i]!=b[j]
f[i,j]=max{f[i-1,j],f[i,j-1],f[i-1,j-1]+1} a[i]=b[j]
边界:
f[i,0]=f[0,j]=0
目标:
f[M,N]
代码实现1:
#include<bits/stdc++.h>
using namespace std;
string a;
string b;
int s[1111][1111];
int main(){
cin>>a>>b;
int x=a.size();
int y=b.size();
for(int i=1;i<=x;i++){
for(int j=1;j<=y;j++){
if(a[i-1]==b[j-1]){
s[i][j]=max(s[i][j],s[i-1][j-1]+1);
}else{
s[i][j]=max(s[i][j-1],s[i-1][j]);
}
}
}
cout<<s[x][y];
return 0;
}
在以下代码中,g[i,j]表示从哪里转移
代码实现2(打印路径):
写一个函数,用于递归输出
#include<bits/stdc++.h>
using namespace std;
char a[1111],b[1111];
int f[1111][1111];
int g[1111][1111];
void s(int i,int j){
if(i==0||j==0){
return;
}
if(!g[i][j]){
s(i-1,j-1);
cout<<a[i-1];
}else if(g[i][j]==1){
s(i-1,j);
}else if(g[i][j]=-1){
s(i,j-1);
}
}
int main(){
cin>>a>>b;
int lena=strlen(a);
int lenb=strlen(b);
for(int i=1;i<=lena;i++){
for(int j=1;j<=lenb;j++){
if(a[i-1]==b[j-1]){
f[i][j]=f[i-1][j-1]+1;
g[i][j]=0;
}else{
if(f[i-1][j]>f[i][j-1]){
f[i][j]=f[i-1][j];
g[i][j]=1;
}else{
f[i][j]=f[i][j-1];
g[i][j]=-1;
}
}
}
}
s(lena,lenb);
return 0;
}
代码实现3(打印路径):
逆序的回溯,使用栈存储路径
#include<bits/stdc++.h>
using namespace std;
char a[1111],b[1111],c;
int f[1111][1111];
int main(){
cin>>a>>b;
int la=strlen(a);
int lb=strlen(b);
for(int i=1;i<=la;i++){
for(int j=1;j<=lb;j++){
if(a[i-1]==b[j-1]){
f[i][j]=f[i-1][j-1]+1;
}else{
f[i][j]=max(f[i-1][j],f[i][j-1]);
}
}
}
int i=la,j=lb;
stack<char>s;
while(f[i][j]){
if(f[i][j]==f[i-1][j]){
i--;
}else if(f[i][j]==f[i][j-1]){
j--;
}else if(f[i][j]>f[i-1][j-1]){
i--;
j--;
s.push(a[i]);
}
}
while(!s.empty()){
c=s.top();
cout<<c;
s.pop();
}
return 0;
}