zoukankan      html  css  js  c++  java
  • 动态规划之最长公共子序列

    动态规划法:

      经常会遇到复杂问题不能简单的分成几个子问题,因而会分出一系列子问题。简单的采用把大问题分解成子问题,并综合子问题解导出大问题的方法,问题求解耗时会按着指数幂级数增加。为了节约重复求相同的子问题的时间,引入一个数组,不管他们是否对最终解有帮助,把所有子问题的解存于该数组中,这就是动态规划采用的基本方法。

      问题:求两个字符序列的最长公共子序列。字符序列指的是从给定字符序列中随意地(不一定连续)去掉若干个字符(可能一个也去不掉)后所形成的子字符串。

    令给定的字符序列X=“x0,x1,…,xm-1”,序列Y=“y0,y1,…,yk-1”是X的子序列,存在X的一个严格递增下标序列<i0,i1,…,ik-1>,使得对所有的j=0,1,…,k-1,有xij=yj。例如,X=“ABCBDAB”,Y=“BCDB”是X的一个子序列。
    考虑最长公共子序列问题如何分解成子问题,设A=“a0,a1,…,am-1”,B=“b0,b1,…,bm-1”,并Z=“z0,z1,…,zk-1”为它们的最长公共子序列。不难证明有以下性质:

    (1) 如果am-1=bn-1,则zk-1=am-1=bn-1,且“z0,z1,…,zk-2”是“a0,a1,…,am-2”和“b0,b1,…,bn-2”的一个最长公共子序列;

    (2) 如果am-1!=bn-1,则若zk-1!=am-1,蕴涵“z0,z1,…,zk-1”是“a0,a1,…,am-2”和“b0,b1,…,bn-1”的一个最长公共子序列;

    (3) 如果am-1!=bn-1,则若zk-1!=bn-1,蕴涵“z0,z1,…,zk-1”是“a0,a1,…,am-1”和“b0,b1,…,bn-2”的一个最长公共子序列。

    这样,在找A和B的公共子序列时,如有am-1=bn-1,则进一步解决一个子问题,找“a0,a1,…,am-2”和“b0,b1,…,bm-2”的一个最长公共子序列;如果am-1!=bn-1,则要解决两个子问题,找出“a0,a1,…,am-2”和“b0,b1,…,bn-1”的一个最长公共子序列和找出“a0,a1,…,am-1”和“b0,b1,…,bn-2”的一个最长公共子序列,再取两者中较长者作为A和B的最长公共子序列。

    求解:

    引进一个二维数组c[][],用c[i][j]记录X[i]与Y[j] 的LCS 的长度,b[i][j]记录c[i][j]是通过哪一个子问题的值求得的,以决定搜索的方向。
    我们是自底向上进行递推计算,那么在计算c[i,j]之前,c[i-1][j-1],c[i-1][j]与c[i][j-1]均已计算出来。此时我们根据X[i] = Y[j]还是X[i] != Y[j],就可以计算出c[i][j]。

    问题的递归式写成:

    回溯输出最长公共子序列过程:

    flow

    算法分析:
    由于每次调用至少向上或向左(或向上向左同时)移动一步,故最多调用(m + n)次就会遇到i = 0或j = 0的情况,此时开始返回。返回时与递归调用时方向相反,步数相同,故算法时间复杂度为Θ(m + n)。

     1 #include<iostream>
     2 #include<vector>
     3 #include<string>
     4 #include<algorithm>
     5 using namespace std;
     6 vector<vector<int>> LCS(string &s1, string &s2 ,int &len)
     7 {
     8     int len1 = s1.size(), len2 = s2.size();
     9     vector<vector<int>> c(len1+1, vector<int>(len2+1,0));
    10     vector<vector<int>> b(len1+1, vector<int>(len2+1,0));
    11     for (int i = 0; i <= len1; i++)
    12         c[i][0] = 0;
    13     for (int j = 0; j <=len2; j++)
    14         c[0][j] = 0;
    15     for(int i=1;i<=len1;i++)
    16         for (int j = 1; j <=len2; j++)
    17         {
    18             if (s1[i-1] == s2[j-1])
    19             {
    20                 c[i][j] = 1 + c[i - 1][j - 1];
    21                 b[i][j] = 0;
    22             }
    23             else if (s1[i-1] != s2[j-1])
    24             {
    25                 if (c[i - 1][j] >= c[i][j - 1])
    26                 {
    27                     c[i][j] = c[i - 1][j];
    28                     b[i][j] = 1;//记录是由哪个子问题求得的解。
    29                 }
    30                 else
    31                 {
    32                     c[i][j] = c[i][j-1];
    33                     b[i][j] = -1;
    34                 }
    35             }
    36         }
    37     len = c[len1][len2];
    38     return b;
    39 }
    40 
    41 
    42 void PrintLCS(vector<vector<int>>& b, string& s1, int len1, int len2)
    43 {
    44     if (len1 == 0 || len2 == 0)
    45         return;
    46     if (b[len1][len2] == 0)
    47     {
    48         PrintLCS(b, s1, len1-1, len2 - 1);
    49         cout << s1[len1 - 1];
    50     }
    51     else if (b[len1][len2] == 1)
    52         PrintLCS(b, s1, len1 - 1, len2 );
    53     else
    54         PrintLCS(b, s1, len1 , len2 - 1);
    55 }
    56 int main()
    57 {
    58     string s1 = "ABCBDAB";
    59     string s2 = "BDCABA";
    60     int len = 0;
    61     vector<vector<int>> b;
    62     b = LCS(s1,s2,len);
    63     cout << len;
    64     PrintLCS(b,s1,s1.size(),s2.size());
    65 }
  • 相关阅读:
    软件体系架构复习要点
    Operating System on Raspberry Pi 3b
    2019-2020 ICPC North-Western Russia Regional Contest
    2019 ICPC ShenYang Regional Online Contest
    2019 ICPC XuZhou Regional Online Contest
    2019 ICPC NanChang Regional Online Contest
    2019 ICPC NanJing Regional Online Contest
    Codeforces Edu Round 72 (Rated for Div. 2)
    Codeforces Round #583 (Div.1+Div.2)
    AtCoder Beginning Contest 139
  • 原文地址:https://www.cnblogs.com/wsw-seu/p/7704822.html
Copyright © 2011-2022 走看看