最长公共子序列(Longest Common Subsequence)

1. 问题描述

给定序列 X 和 Y,若 S 同时为 X 和 Y 的子序列,则称 S 为 X 和 Y 的公共子序列;若 S 还是 X 和 Y 的所有公共子序列中最长的,则称 S 是 X 和 Y 的一个最长公共子序列。

 

注意区别最长公共子串(要求连续)和最长公共子序列(不要求连续)。

 

最长公共子序列不唯一。

 

2. 动态规划解

用 opt[i, j] 表示 X[1 : i] 和 Y[1 : j] 的最长公共子序列的长度。则显然有边界条件:

  • opt[0, j] = 0
  • opt[i, 0] = 0

 

另外还有递归方程:

  • 如果 Xi == Yj,则 opt[i, j] == 1 + opt[i - 1, j - 1]
  • 如果 Xi!= Yj,则 opt[i, j] = max(opt[i - 1, j], opt[i, j - 1])

 

3. c++实现

  • 只需要知道最长公共子序列的长度,不需要知道最长公共子序列是什么:
template<class Iter1, class Iter2>
int LCS(const Iter1 &beg1, const Iter1 &end1, const Iter2 &beg2, const Iter2 &end2)
{
    int m = end1 - beg1;
    int n = end2 - beg2;
    std::vector<int> opt1(n + 1, 0);
    std::vector<int> opt2(n + 1, 0);

    Iter1 id1 = beg1;
    Iter2 id2;
    for(int i = 1; i <= m; ++i, ++id1)
    {
        id2 = beg2;
        for(int j = 1; j <= n; ++j, ++id2)
            opt2[j] = (*id1 == *id2) ? (opt1[j - 1] + 1) :
                std::max(opt1[j], opt2[j - 1]);
        if(i < m)
            opt1.swap(opt2);
    }

    return opt2[n];
}

 

  • 需要知道最长公共子序列是什么:输出最长公共子序列在第一个输入序列中的下标(从 0 开始)
template<class Iter1, class Iter2>
std::vector<int>
LCS2(const Iter1 &beg1, const Iter1 &end1, const Iter2 &beg2, const Iter2 &end2)
{
    typedef std::vector<std::vector<int>> Matrix;
    int m = end1 - beg1;
    int n = end2 - beg2;
    Matrix opt(m + 1, std::vector<int>(n + 1));
    Matrix state(m + 1, std::vector<int>(n + 1));

    for(int i = 0; i <= m; ++i)
        opt[i][0] = 0;
    for(int j = 0; j <= n; ++j)
        opt[0][j] = 0;

    Iter1 id1 = beg1;
    Iter2 id2;
    for(int i = 1; i <= m; ++i, ++id1)
    {

        id2 = beg2;
        for(int j = 1; j <= n; ++j, ++id2)
        {
            if(*id1 == * id2)
            {
                state[i][j] = 0;
                opt[i][j] = opt[i - 1][j - 1] + 1;
            }
            else if(opt[i - 1][j] > opt[i][j - 1])
            {
                state[i][j] = 1;
                opt[i][j] = opt[i - 1][j];
            }
            else
            {
                state[i][j] = 2;
                opt[i][j] = opt[i][j - 1];
            }
        }
    }
    

    std::vector<int> ans(opt[m][n]);
    int i = m, j = n, id = opt[m][n] - 1;
    while(id >= 0)
    {
        if(state[i][j] == 0)
        {
            ans[id--] = i - 1;
            --i;
            --j;
        }
        else if(state[i][j] == 1)
            --i;
        else
            --j;
    }
    return ans;
}

 

4. 分析

时间复杂度 O(m*n)

空间复杂度分别为 O(n) 和 O(m * n)

 

 

posted @ 2013-09-20 16:50  半亩梨花  阅读(289)  评论(0编辑  收藏  举报