问题描述:
给两个字符序列A和B,求长度最长的公共字序列,并将最长公共字序列输出来。例子如下:A序列ABCBDAB,B序列BDCABA则最长公共子序列是BDAB。
分析:
之前就有了解这个方面的内容,也知道大概思路。就是判断两个序列的最后一位是否相同,若相同则继续判断i-1和j-1是否相同;若不相同则比较i和j-1两者谁更大,继续比较更大的哪一个,下面就是代码:
d(i,j)为A1,A2,A3,...,Ai和B1,B2,B3,...,Bj的LCS长度,
当A[i]==B[j]时,d(i,j)=d(i-1,j-1)+1;
否则d(i,j)=max(d(i-1,j),d(i,j-1));
但是,就是知道这样的思路,我还是写不出来代码,就是写出来了也是答案不对的。上面的式子要求必须先知道d(i-1,j-1)或者d(i-1,j)或者d(i,j-1)的值才可以解决掉。因此不能逆序来实现这个。因此写不下去的代码,后来在网上扒代码,发现他们都是顺序实现的,当时百思不得其解,后来有人提到到了数字三角形,于是乎,就觉得对啊,求数字三角形时也行要得到最后来获取结果,而它就是从底部向顶部上计算而去的,只不过就是在最后一行的时候就构造了一组数据为零的。于是就很自然的就得到了最后的结果。这个方法在这里一样适用,其实应该很好就应该想到,因为你无法计算d(i-1,j-1)的值就不能写代码,必须解决的问题,自然而然就想到了由底向上的方法。
但是在扒代码的过程中我发现了一张图表,当时也觉得看不懂是怎么填的数,后来就看懂了啊,就是根据状态转移方程试来写的,就很简单可以写出来。打印路径的话,只需要添加一个变量,这个变量可以知道dp(i,j)是根据dp(i-1,j-1)dp(i-1,j)d(i,j-1)哪一个量得到的,最后递归输出它的最长公共子序列。
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
const int maxx=1000+10;
string s1,s2;
int c[maxx][maxx],d[maxx][maxx];
void printLcs(int i,int j)
{
if(i==0||j==0)
return ;
if(d[i][j]==1)
{
printLcs(i-1,j-1);
cout<<s1[i-1]<<" ";
}
else if(d[i][j]==2)
{
printLcs(i-1,j);
}
else if(d[i][j]==3)
{
printLcs(i,j-1);
}
}
int main()
{
while(cin>>s1>>s2){
int a=s1.length();
int b=s2.length();
memset(d,0,sizeof(d));
for(int i=0;i<=a;i++)
c[i][0]=0;
for(int j=0;j<=b;j++)
c[0][j]=0;
for(int i=1;i<=a;i++)
for(int j=0;j<=b;j++)
{
if(s1[i-1]==s2[j-1])
{
c[i][j]=c[i-1][j-1]+1;
d[i][j]=1;
}
else if(c[i-1][j]>c[i][j-1])
{
c[i][j]=c[i-1][j];
d[i][j]=2;
}
else
{
c[i][j]=c[i][j-1];
d[i][j]=3;
}
}
cout<<c[a][b]<<endl;
printLcs(a,b);
}
return 0;
}