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

    转自http://blog.csdn.net/yysdsyl/article/details/4226630

    动态规划法

    经常会遇到复杂问题不能简单地分解成几个子问题,而会分解出一系列的子问题。简单地采用把大问题分解成子问题,并综合子问题的解导出大问题的解的方法,问题求解耗时会按问题规模呈幂级数增加。

    为了节约重复求相同子问题的时间,引入一个数组,不管它们是否对最终解有用,把所有子问题的解存于该数组中,这就是动态规划法所采用的基本方法。

    【问题】 求两字符序列的最长公共字符子序列

    问题描述:字符序列的子序列是指从给定字符序列中随意地(不一定连续)去掉若干个字符(可能一个也不去掉)后所形成的字符序列。令给定的字符序列X=“x0,x1,…,xm-1,序列Y=“y0,y1,…,yk-1X的子序列,存在X的一个严格递增下标序列<i0,i1,…,ik-1>,使得对所有的j=0,1,…,k-1,有xij=yj。例如,X=“ABCBDAB”,Y=“BCDB”是X的一个子序列。

    考虑最长公共子序列问题如何分解成子问题,设A=“a0,a1,…,am-1B=“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]。


    问题的递归式写成:

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

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

     1 #include <stdio.h>
    2 #include <string.h>
    3 #define MAXLEN 100
    4
    5 void LCSLength(char *x, char *y, int m, int n, int c[][MAXLEN], int b[][MAXLEN])
    6 {
    7 int i, j;
    8
    9 for(i = 0; i <= m; i++)
    10 c[i][0] = 0;
    11 for(j = 1; j <= n; j++)
    12 c[0][j] = 0;
    13 for(i = 1; i<= m; i++)
    14 {
    15 for(j = 1; j <= n; j++)
    16 {
    17 if(x[i-1] == y[j-1])
    18 {
    19 c[i][j] = c[i-1][j-1] + 1;
    20 b[i][j] = 0;
    21 }
    22 else if(c[i-1][j] >= c[i][j-1])
    23 {
    24 c[i][j] = c[i-1][j];
    25 b[i][j] = 1;
    26 }
    27 else
    28 {
    29 c[i][j] = c[i][j-1];
    30 b[i][j] = -1;
    31 }
    32 }
    33 }
    34 }
    35
    36 void PrintLCS(int b[][MAXLEN], char *x, int i, int j)
    37 {
    38 if(i == 0 || j == 0)
    39 return;
    40 if(b[i][j] == 0)
    41 {
    42 PrintLCS(b, x, i-1, j-1);
    43 printf("%c ", x[i-1]);
    44 }
    45 else if(b[i][j] == 1)
    46 PrintLCS(b, x, i-1, j);
    47 else
    48 PrintLCS(b, x, i, j-1);
    49 }
    50
    51 int main(int argc, char **argv)
    52 {
    53 char x[MAXLEN] = {"ABCBDAB"};
    54 char y[MAXLEN] = {"BDCABA"};
    55 int b[MAXLEN][MAXLEN];
    56 int c[MAXLEN][MAXLEN];
    57 int m, n;
    58
    59 m = strlen(x);
    60 n = strlen(y);
    61
    62 LCSLength(x, y, m, n, c, b);
    63 PrintLCS(b, x, m, n);
    64
    65 return 0;
    66 }

      



  • 相关阅读:
    poj 2584 T-Shirt Gumbo (二分匹配)
    hdu 1757 A Simple Math Problem (乘法矩阵)
    矩阵之矩阵乘法(转载)
    poj 2239 Selecting Courses (二分匹配)
    hdu 3661 Assignments (贪心)
    hdu 1348 Wall (凸包)
    poj 2060 Taxi Cab Scheme (二分匹配)
    hdu 2202 最大三角形 (凸包)
    hdu 1577 WisKey的眼神 (数学几何)
    poj 1719 Shooting Contest (二分匹配)
  • 原文地址:https://www.cnblogs.com/DanielZheng/p/2148660.html
Copyright © 2011-2022 走看看