最长公共子序列(LCS)

来源:互联网 发布:收购网络域名诈骗保护 编辑:程序博客网 时间:2024/06/07 02:07

问题描述:
给两个字符序列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;}
0 0