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

5 篇文章 0 订阅
4 篇文章 0 订阅

问题的定义:

  • 子序列
    • X=(A, B, C, B, D, B)
    • Z=(B, C, D, B)是X的子序例
    • W=(B, D, A)不是X的子序例
  • 公共子序列
    • Z是序列X与Y的公共子序列如果Z是X的子序也是Y的子序列。

最长公共子序列(LCS)问题

输入:X = (x1, x2, …, xn),Y = (y1, y2, …, ym)
输出:Z = X与Y的最长公共子序列

蛮力法:

  • 枚举X的每个子序列Z;
  • 检查Z是否为Y的子序列;
  • T(n)=O(m×2n)。

动态规划:

  • 优化子结构:
    设X = (x1, x2, …, xn),Y = (y1, y2, …, ym) 是两个序列,Z=(z1, z2, …, zn)是X与Y的LCS,我们有:

    1. 如果xm = yn, 则zk = xm = yn, Zk-1是Xm-1和Yn-1的LCS,即,LCSXY = LCSXm-1Yn-1+ (xm = yn)。
    2. 如果xm ≠ yn,且zk ≠ xm,则Z是Xm-1和Y的LCS,即LCSXY= LCSXm-1Y
    3. 如果xm ≠ yn,且zk ≠ yn,则Z是X和Yn-1的LCS,即LCSXY= LCSXYn-1
  • 递归方程:

    • C[i, j] = Xi与Yj 的LCS的长度
    • LCS长度的递归方程
      1. C[i, j] = 0 , if i=0 or j=0
      2. C[i, j] = C[i-1, j-1] + 1, if i, j>0 and xii = yjj
      3. C[i, j] = Max(C[i, j-1], C[i-1, j]), if i, j>0 and xii  yjj
  • 基本思想:
    在知道C[i-1, j-1], C[i, j-1], C[i-1, j]的情况下,根据递归方程计算C[i, j]。
    基本思想

  • 数据结构:

    1. C[0:m,0:n]: C[i,j]是Xi与Yj的LCS的长度
    2. B[1:m,1:n]: B[i,j]是指针,指向计算C[i,j]时所选择的子问题的优化解所对应的C表的表项

伪代码:

LCS-length(X, Y)
    m = length(X);
    n = length(Y);
    For i = 1 To m Do 
        C[i,0] = 0;
    For j = 1 To n Do 
        C[0,j] = 0;
    For i = 1 To m Do
        For j = 1 To n Do
            If xi = yj Then 
                C[i,j] = C[i-1,j-1] + 1;
                B[i,j] = “↖”;
            Else If C[i-1,j]>=C[i,j-1] Then
                C[i,j] = C[i-1,j]; 
                B[i,j] = “↑”;
            Else C[i,j] = C[i,j-1];
                 B[i,j] = “←”;
Return C and B.

C++代码:

#include <iostream>
#include <vector>
#include <string>

using namespace std;

vector<vector<int>> b, c;


void lcs(string str1, string str2, int len1, int len2)
{
    b.resize(len1 + 1);
    c.resize(len1 + 1);
    for (int i = 0; i < len1 + 1; i++)
    {
        b[i].resize(len2 + 1, 0);
        c[i].resize(len2 + 1, 0);
    }

    for (int i = 1; i <= len1; i++)
    {
        for (int j = 1; j <= len2; j++)
        {
            if (str1[i - 1] == str2[j - 1])
            {
                c[i][j] = c[i - 1][j - 1] + 1;
                b[i][j] = 0;
            }
            else if(c[i - 1][j] >= c[i][j - 1])
            {
                c[i][j] = c[i - 1][j];
                b[i][j] = 1;
            }
            else
            {
                c[i][j] = c[i][j - 1];
                b[i][j] = -1;
            }
        }
    }   
}

void PrintLCS(vector<vector<int>> b, string str1, int i, int j)
{
    if (i == 0 || j == 0)
        return;
    if (b[i][j] == 0)
    {
        PrintLCS(b, str1, i - 1, j - 1);
        cout << str1[i - 1] << " ";
    }
    else if (b[i][j] == 1)
    {
        PrintLCS(b, str1, i - 1, j);
    }
    else
    {
        PrintLCS(b, str1, i, j - 1);
    }
}


int main()
{   
    string str1 = "abcdef";
    string str2 = "acef";

    int len1 = str1.size();
    int len2 = str2.size();
    b.resize(len1 + 1);
    c.resize(len2 + 1);

    lcs(str1, str2, len1, len2);

    cout << c[len1][len2] << endl;

    PrintLCS(b, str1, len1, len2);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值