数据结构 : 动态规划学习三 | 最长公共子序列

动态规划学习三 | 最长公共子序列

动态规划 之 最长公共子序列 过程图

  • 求最长公共子序列的长度
  • 倒推构造最长公共子序列

算法推导:动态规划算法解最长公共子序列LCS问题

记:

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)}。

Procedure LCS_LENGTH(X,Y);
begin
  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 x[i]=y[j] then
        begin
          c[i,j]:=c[i-1,j-1]+1;
          b[i,j]:="↖";
        end
      else if c[i-1,j]≥c[i,j-1] then
        begin
          c[i,j]:=c[i-1,j];
          b[i,j]:="↑";
        end
      else
        begin
          c[i,j]:=c[i,j-1];
          b[i,j]:="←"
        end;
  return(c,b);

代码实现

1. 计算LCS长度

伪ä"£ç 2

2 . 构造一个LCS

LCS

C++代码实现

#include "MyInclude.h"

int lcs(string str1, string str2, vector <vector<int> >&dir) {

    int len1 = str1.size();
    int len2 = str2.size();

    // c[i][j] 表示 str1[0..i-1]与st2[0..j-1]的最长公共子序列长度
    vector<vector<int>>  c(len1 + 1, vector<int>(len2 + 1, 0));

    for (int i = 1; i < len1 + 1; ++i) {
        for (int j = 1; j < len2 + 1; ++j) {
            if (str1[i - 1] == str2[j - 1]) {
                // 最后一个字符相等
                c[i][j] = c[i - 1][j - 1] + 1;
                dir[i][j] = 0; // 左上
            }
            else {
                // 最后一个字符不相等
                if (c[i - 1][j]>=c[i][j - 1]) {
                    c[i][j] = c[i - 1][j];
                    dir[i][j] = 1; // 上
                } else {
                    c[i][j] = c[i][j - 1];
                    dir[i][j] = 2; // 左
                }
            }
        }
    }
    // 返回最大长度
    return c[len1][len2];
}


void print_lcs(vector < vector<int> >&dir, string str, int i, int j) {
    if (i == 0 || j == 0) {
        return;
    }

    if (dir[i][j] == 0) {
        print_lcs(dir, str, i - 1, j - 1);
        std:cout << str[i-1];
    }
    else if (dir[i][j] == 1) {
        print_lcs(dir, str, i - 1, j );
    }
    else {
        print_lcs(dir, str, i , j - 1);
    }
}

int main() {


    string s1 = "abcd";
    string s2 = "bd";

    int len1 = s1.size();
    int len2 = s2.size();

    vector<vector<int>>  dir(len1 + 1, vector<int>(len2 + 1, 0));
    int res = lcs(s1, s2, dir);

    print_lcs(dir, s1, len1 , len2 );

    system("pause");
    return 0;
}

扩展1: 如何获得所有的LCS

set<string> setOfLCS;

int lcs2(string str1, string str2, vector < vector<int>> &c) {
    int len1 = str1.size();
    int len2 = str2.size();

    c.resize(len1 + 1);
    for (int i = 0; i <= len1; i++) {
        c[i].resize(len2 + 1);
    }

    for (int i = 0; i <= len1; i++) {
        c[i][0] = 0;
    }
    for (int i = 0; i <= len2; i++) {
        c[0][i] = 0;
    }

    for (int i = 1; i < len1 + 1; ++i) {
        for (int j = 1; j < len2 + 1; ++j) {
            if (str1[i - 1] == str2[j - 1]) {
                // 最后一个字符相等
                c[i][j] = c[i - 1][j - 1] + 1;
            } else {
                // 最后一个字符不相等
                if (c[i - 1][j] >= c[i][j - 1]) {
                    c[i][j] = c[i - 1][j];
                } else {
                    c[i][j] = c[i][j - 1];
                }
            }
        }
    }
    return c[len1][len2];
}


// 打印所有的最长公共子序列
void trackBack(vector< vector<int> >& c, string str1,string str2, int i, int j,string lcs) {

    while (i != 0 && j != 0) {

        if (str1[i-1] == str2[j-1]) {
            lcs.push_back(str1[i-1]);
            i--;
            j--;
        } else{
            // 字符不相等
            if (c[i - 1][j] > c[i][j - 1]) {
                i--;
            }
            else if (c[i - 1][j] < c[i][j - 1]) {
                j--;
            }
            else {
                trackBack(c, str1, str2, i - 1, j, lcs);
                trackBack(c, str1, str2, i, j - 1, lcs);
                return;
            }
        }
    }
    reverse(lcs.begin(), lcs.end());
    setOfLCS.insert(lcs);
}


int main() {
    string s1 = "ABCBDAB";
    string s2 = "BDCABA";

    int len1 = s1.size();
    int len2 = s2.size();

    vector<vector<int>> dirs;
    lcs2(s1, s2, dirs);
    string lcs;
    trackBack(dirs, s1, s2, len1 + 1, len2 + 1, lcs);

    for (auto x : setOfLCS) {
        std::cout << x << endl;
    }


    system("pause");
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qjh5606/article/details/86549960