lcs最长公共子序列


题目:如果字符串一的所有字符按其在字符串中的顺序出现在另外一个字符串二中,
则字符串一称之为字符串二的子串。

注意,并不要求子串(字符串一)的字符必须连续出现在字符串二中。
请编写一个函数,输入两个字符串,求它们的最长公共子串,并打印出最长公共子串。
例如:输入两个字符串BDCABA和ABCBDAB,字符串BCBA和BDAB都是是它们的最长公共子序列,则输出它们的长度4,并打印任意一个子序列。

分析:求最长公共子序列(Longest Common Subsequence, LCS)是一道非常经典的动态规划题,因此一些重视算法的公司像MicroStrategy都把它当作面试题。

事实上,最长公共子序列问题也有最优子结构性质。

记:

Xi=﹤x1,⋯,xi﹥即X序列的前i个字符 (1≤i≤m)(前缀)

Yj=﹤y1,⋯,yj﹥即Y序列的前j个字符 (1≤j≤n)(前缀)

假定Z=﹤z1,⋯,zk﹥∈LCS(X , Y)。

  • xm=yn(最后一个字符相同),则不难用反证法证明:该字符必是X与Y的任一最长公共子序列Z(设长度为k)的最后一个字符,即有zk = xm = yn 且显然有Zk-1∈LCS(Xm-1 , Yn-1)即Z的前缀Zk-1是Xm-1与Yn-1的最长公共子序列。此时,问题化归成求Xm-1与Yn-1的LCS(LCS(X , Y)的长度等于LCS(Xm-1 , Yn-1)的长度加1)。

  • xm≠yn,则亦不难用反证法证明:要么Z∈LCS(Xm-1, Y),要么Z∈LCS(X , Yn-1)。由于zk≠xm与zk≠yn其中至少有一个必成立,若zk≠xm则有Z∈LCS(Xm-1 , Y),类似的,若zk≠yn 则有Z∈LCS(X , Yn-1)。此时,问题化归成求Xm-1与Y的LCS及X与Yn-1的LCS。LCS(X , Y)的长度为:max{LCS(Xm-1 , Y)的长度, LCS(X , Yn-1)的长度}。

由于上述当xm≠yn的情况中,求LCS(Xm-1 , Y)的长度与LCS(X , Yn-1)的长度,这两个问题不是相互独立的:两者都需要求LCS(Xm-1,Yn-1)的长度。另外两个序列的LCS中包含了两个序列的前缀的LCS,故问题具有最优子结构性质考虑用动态规划法。

也就是说,解决这个LCS问题,你要求三个方面的东西:1、LCS(Xm-1,Yn-1)+1;2、LCS(Xm-1,Y),LCS(X,Yn-1);3、max{LCS(Xm-1,Y),LCS(X,Yn-1)}

、最长公共子序列的结构

最长公共子序列的结构有如下表示:

设序列X=<x1, x2, …, xm>和Y=<y1, y2, …, yn>的一个最长公共子序列Z=<z1, z2, …, zk>,则:

  1. 若xm=yn,则zk=xm=yn且Zk-1是Xm-1和Yn-1的最长公共子序列;
  2. 若xm≠yn且zk≠xm ,则Z是Xm-1和Y的最长公共子序列;
  3. 若xm≠yn且zk≠yn ,则Z是X和Yn-1的最长公共子序列。

其中Xm-1=<x1, x2, …, xm-1>,Yn-1=<y1, y2, …, yn-1>,Zk-1=<z1, z2, …, zk-1>。

、子问题的递归结构

由最长公共子序列问题的最优子结构性质可知,要找出X=<x1, x2, …, xm>和Y=<y1, y2, …, yn>的最长公共子序列,可按以下方式递归地进行:当xm=yn时,找出Xm-1和Yn-1的最长公共子序列,然后在其尾部加上xm(=yn)即可得X和Y的一个最长公共子序列。当xm≠yn时,必须解两个子问题,即找出Xm-1和Y的一个最长公共子序列及X和Yn-1的一个最长公共子序列。这两个公共子序列中较长者即为X和Y的一个最长公共子序列。

由此递归结构容易看到最长公共子序列问题具有子问题重叠性质。例如,在计算X和Y的最长公共子序列时,可能要计算出X和Yn-1及Xm-1和Y的最长公共子序列。而这两个子问题都包含一个公共子问题,即计算Xm-1和Yn-1的最长公共子序列。

与矩阵连乘积最优计算次序问题类似,我们来建立子问题的最优值的递归关系。用c[i,j]记录序列Xi和Yj的最长公共子序列的长度。其中Xi=<x1, x2, …, xi>,Yj=<y1, y2, …, yj>。当i=0或j=0时,空序列是Xi和Yj的最长公共子序列,故c[i,j]=0。其他情况下,由定理可建立递归关系如下:

#include<iostream>
#include<cstring>
#include<vector>
using namespace std;
struct Lcs{
    int left=0,top=0,data=0;
};
Lcs **lcs;
void calLcs(char *ch1,char *ch2,int ch1size,int ch2size){
    int i,j,k;
    for(i=1;i<=ch1size;i++){
        for(j=1;j<=ch2size;j++){
            if(ch1[i]==ch2[j]){
                lcs[i][j].data=lcs[i-1][j-1].data+1;
                lcs[i][j].left=-1;lcs[i][j].top=-1;
            }else{
                if(lcs[i][j].data<=lcs[i][j-1].data){
                    lcs[i][j].data=lcs[i][j-1].data;
                    lcs[i][j].left=-1;
                }else{
                    lcs[i][j].data=lcs[i-1][j].data;
                    lcs[i][j].top=-1;
                }
            }

        }
    }
}
int main(){
    char ch1[1000],ch2[1000];
    cout<<"ÊäÈë×Ö·û´®£º"<<endl;
    cin>>(ch1+1);
    cin>>(ch2+1);
    int ch1size=strlen(ch1+1),ch2size=strlen(ch2+1),i,j;
    lcs=new Lcs*[ch1size+1];
    for(i=0;i<=ch1size;i++){
        lcs[i]=new Lcs[ch2size+1];
    }
    calLcs(ch1,ch2,ch1size,ch2size);
    int indexi=0,indexj=0,maxnum=0;
    for(i=1;i<=ch1size;i++){
        for(j=1;j<=ch2size;j++){
            if(lcs[i][j].data>maxnum){
                indexi=i;
                indexj=j;
                maxnum=lcs[i][j].data;
            }
        }
    }
    vector<int>v;
    for(i=indexi;i>=1;){
        for(j=indexj;j>=1;){
            if(i<1) break;
            if(lcs[i][j].top==-1&&lcs[i][j].left==-1){
                v.push_back(i);
                j--;i--;
                continue;
            }else{
                if(lcs[i][j].top==-1){
                    i--;
                    continue;
                }

                else if(lcs[i][j].left==-1){
                    j--;
                    continue;
                }
                else j--;
            }
        }
        i--;
        if(i<1)break;
    }
    for(i=v.size()-1;i>=0;i--)
        cout<<ch1[v[i]];
    return 0;
}


猜你喜欢

转载自blog.csdn.net/naocanmani/article/details/70339956
今日推荐