zoukankan      html  css  js  c++  java
  • 最长公共子序列(LCS)

      以前一直不懂LCS问题,然而找工作被LCS折磨的,与那个公司无缘了。

      最长公共子序列问题是一道经典的动态规划问题,最长公共子序列问题也有最优子结构。

    即: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);

    若: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)}

    由上述可以得出递归公式:

    #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;
    }
  • 相关阅读:
    转载:Python十分钟入门
    Think Python: How to Think Like a Computer Scientist
    LeetCode(34):搜索范围
    LeetCode(33):搜索旋转排序数组
    LeetCode(32):最长有效括号
    LeetCode(31): 下一个排列
    LeetCode(30):与所有单词相关联的字串
    LeetCode(29): 两数相除
    LeetCode(28): 实现strStr()
    LeetCode(27): 移除元素
  • 原文地址:https://www.cnblogs.com/awy-blog/p/3987607.html
Copyright © 2011-2022 走看看