1. 程式人生 > >lcs最長公共子序列

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;
}