给出两个字符串A B,求A与B的最长公共子序列(子序列不要求是连续的)。
比如两个串为:
abcicba
abdkscab
ab是两个串的子序列,abc也是,abca也是,其中abca是这两个字符串最长的子序列。
Input第1行:字符串A
第2行:字符串B
(A,B的长度 <= 1000)Output输出最长的子序列,如果有多个,随意输出1个。Sample Input
abcicba
abdkscab
Sample Output
abca
提供两种解法:
dp[ i+1 ][ j+1 ]=dp[ i ][ j ]+1 ( a[i]==b[j] )
dp[ i+1 ][ j+1 ]=max(dp[ i ][ j+1 ] , dp[ i+1 ][ j ] ) (其他)
dp[i + 1][j + 1]表示的是在a串前i位和b串前j位的Lcs,观察下图,容易发现,只有a[i] == b[i]的情况才是打印路径所需要的步骤。
(1)利用dfs的回溯作用记录路径,最后打印出来。
//咱们做题前得先把思路捋清
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e3+10;
int dp[maxn][maxn],pre[maxn][maxn];
char a[maxn],b[maxn];
vector<char>ans;
void dfs(int x,int y)
{
if(x==0 || y==0)
return;
else if(pre[x][y]==1)
{
dfs(x-1,y-1);
ans.push_back(a[x-1]);
}
else if(pre[x][y]==-1)
dfs(x-1,y);
else
dfs(x,y-1);
}
int main()
{
ios::sync_with_stdio(false);
cin>>a>>b;
int len1 = strlen(a);
int len2 = strlen(b);
for(int i=0;i<len1;i++)
for(int j=0;j<len2;j++)
{
if(a[i]==b[j])
{
dp[i+1][j+1]=dp[i][j]+1;
pre[i+1][j+1] = 1; //记录从左上方来的
}
else
{
if(dp[i][j+1]>dp[i+1][j])
{
dp[i+1][j+1] = dp[i][j+1];
pre[i+1][j+1] = -1; //记录从左方来的
}
else
{
dp[i+1][j+1] = dp[i+1][j];
pre[i+1][j+1] = 0; //记录从上方来的
}
}
}
//输出Lcs的最大长度
dfs(len1,len2);
for(int i=0;i<ans.size()-1;i++)
cout<<ans[i];
cout<<ans[ans.size()-1]<<endl;
return 0;
}
(2)自己看了"牛神"的代码,一瘸一拐的勉强写下来了,不容易。AC代码如下:
#include<iostream>
#include<string.h>
#include<algorithm>
using namespace std;
int dp[1001][1001];
int main()
{
int i,j;
char s1[1001],s2[1001],s3[1001];
cin>>s1>>s2;
int k=strlen(s1);
int l=strlen(s2);
memset(dp,0,sizeof(dp));
for(i=0;i<k;i++)
for(j=0;j<l;j++)
{
if(s1[i]==s2[j])
dp[i+1][j+1]=dp[i][j]+1;
else
dp[i+1][j+1]=max(dp[i+1][j],dp[i][j+1]);
}
int len=dp[k][l];
memset(s3,'\0',sizeof(s3));
for(;dp[k][l]!='\0';)
{
if(dp[k][l]==dp[k-1][l])
k--;
else if(dp[k][l]==dp[k][l-1])
l--;
else
{
s3[--len]=s2[l-1];
k--;
l--;
}
}
cout<<s3<<endl;
return 0;
}