最長公共子序列(LCS)

  之前一直不懂LCS問題,然而找工做被LCS折磨的,與那個公司無緣了。ios

  最長公共子序列問題是一道經典的動態規劃問題,最長公共子序列問題也有最優子結構。spa

即:Xi<x1, x2,..., xi>即X序列的前i個字符(1<=i<=m),Yj<y1, y2,..., yj)即Y序列的前j個序列(1<=j<=n);假設Z<z1,z2,...,Zk>屬於LCS(X,Y);code

若: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)。blog

若: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,故問題具備最優子結構性質考慮用動態規劃法。string

    也就是說,解決這個LCS問題,你要求三個方面的東西:一、LCS(Xm-1,Yn-1)+1;二、LCS(Xm-1,Y),LCS(X,Yn-1);三、max{LCS(Xm-1,Y),LCS(X,Yn-1)}it

由上述能夠得出遞歸公式:io

 

#include <iostream>
#include <vector>
#include <cstring>
using namespace std;
enum decreaseDir {kInit = 0, kLeft, kUp, kLeftUp};
string ret;
void LCS_Print(vector<vector<int> > &dir, const char *str1, const char *str2, int row, int col)
{
    if(str1 == NULL || str2 == NULL)
        return;
    int len1 = strlen(str1);
    int len2 = strlen(str2);
    if(len1 == 0 || len2 == 0 || !(row < len1 && col < len2 ))
        return;
    if(dir[row][col] == kLeftUp)
    {
        if(row > 0 && col > 0)
            LCS_Print(dir, str1, str2, row - 1, col -1);
        ret.push_back(str1[row]);
    }
    else if(dir[row][col] == kUp)
    {
        if(row > 0)
            LCS_Print(dir, str1, str2, row - 1, col);
    }
    else if(dir[row][col] == kLeft)
    {
        if(col > 0)
            LCS_Print(dir, str1, str2, row, col -1);
    }
}
int LCS(const char *str1, const char *str2)
{
    if(!str1 || !str2)
        return 0;
    int len1 = strlen(str1);
    int len2 = strlen(str2);
    if(!len1 || !len2)
        return 0;
    int i, j;
    vector<vector<int> > dp(len1, vector<int>(len2, 0));
    vector<vector<int> > dir(len1, vector<int>(len2, 0));
    for(int i = 0; i < len1; i++)
    {
        for(int j = 0; j < len2; j++)
        {
            if(i == 0 || j == 0)
            {
                if(str1[i] == str2[j])
                {
                    dp[i][j] = 1;
                    dir[i][j] = kLeftUp;
                }
                else
                {
                    if(i > 0)
                    {
                        dp[i][j] = dp[i-1][j];
                        dir[i][j] = kUp;
                    }
                    if(j > 0)
                    {
                        dp[i][j] = dp[i][j-1];
                        dir[i][j] = kLeft;
                    }
                }
            }
            else if(str1[i] == str2[j])
            {
                dp[i][j] = dp[i-1][j-1] + 1;
                dir[i][j] = kLeftUp;
            }
            else if(dp[i-1][j] > dp[i][j-1])
            {
                dp[i][j] = dp[i-1][j];
                dir[i][j] = kUp;
            }
            else
            {
                dp[i][j] = dp[i][j-1];
                dir[i][j] = kLeft;
            }
        }
    }
    LCS_Print(dir, str1, str2, len1 - 1, len2 - 1);
    return dp[len1-1][len2-1];
}
int main()
{
    const char *str1 = "abcde";
    const char *str2 = "acde";
    ret.clear();
    int longest = LCS(str1, str2);
    cout << "longest = " << longest << endl;
    cout << "ret = " << ret << endl;
    return 0;
}
相關文章
相關標籤/搜索