关闭

动态规划:最长公共子序列问题

标签: 动态规划最长公共子序列LCS
467人阅读 评论(0) 收藏 举报
分类:

最长公共子序列问题(Longest-common-subsequence Problem)简称LCS问题。题目为给定两个序列X、Y求它们的LCS(最长公共子序列),这里的子序列Z的定义为:Z中的元素既在X中也在Y中,并且他们在X、Y中满足严格的下标为一个增序列(假设下标从左到右依次增大)。另外,不要求Z中的元素在X、Y中是连续的,比如当A = {A, B, C, D, B, C, A},B = {B, C, A, D}时,可以有Z = {B, C, D}。现在我们需要求的是LCS,即符合这种条件的最长的一个公共子序列。

而LCS问题是一类动态规划(DP:dynamic programming)问题 。其具有最优子结构性质,定义如下:

设序列 X = {x1,x2,...,xm} 和 Y = {y1,y2,...yn} 的一个最长公共子序列 Z = {z1,z2,...,zk},则:
    1> 若 xm=yn,则 zk=xm=yn,且Zk-1是Xm-1和Yn-1的最长公共子序列; 
  2> 若 xm≠yn且 zk≠xm ,则 Z是 Xm-1和 Y的最长公共子序列;
    3> 若 xm≠yn且 zk≠yn ,则 Z是 X和 Yn-1的最长公共子序列;
    其中Xm-1={x1,x2,...,xm-1},Yn-1={y1,y2,...yn-1},Zk-1={z1,z2,...,zk-1}。

上面的结论很容易证明。


下面贴上算法导论中的伪代码(其中的数组b可以不用理会,c用于存储Xm和Yn的LCS):


源代码如下(注意:我的源代码中的n是序列X的长度,m是序列Y的长度):

/*  计算LCS,动态规划从前往后推算  */
#include <iostream>
#include <stdio.h>
#include <memory.h>
#define N 1000
char X[N], Y[N];                //序列X、Y
int C[N][N];                    //Cij为Xi和Yj的最长公共子序列
int lcs(int n, int m);          //计算最长公共子序列
void getAnswer(int n, int m);   //构建答案

int main()
{
    int m, n;
    printf("Enter n and m: ");
    while(2 == scanf("%d%d", &n, &m))
    {
        printf("Enter %d elements for X:", n);
        while(getchar() != '\n') continue;      //清除回车符等
        for(int i = 1; i <= n; ++i)
            scanf("%c", &X[i]);
        printf("Enter %d elements for Y:", m);
        while(getchar() != '\n') continue;
        for(int i = 1; i <= m; ++i)
            scanf("%c", &Y[i]);
        printf("MaxLength: %d\nLCS:", lcs(n, m));
        getAnswer(n, m);
        printf("\nEnter n and m: ");
    }
    return 0;
}

int lcs(int n, int m)
{
    for(int i = 0; i <= n; ++i)
        C[i][0] = 0;
    for(int i = 0; i <= m; ++i)
        C[0][i] = 0;

    for(int i = 1; i <= n; ++i)
    {
        for(int j = 1; j <= m; ++j)
        {
            if(X[i] == Y[j])
                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[n][m];
}

void getAnswer(int n, int m)
{
    if(0 == n || 0 == m)
        return;
    if((C[n][m] == C[n-1][m-1] + 1) && (X[n] == Y[m]))  //一定要判断X[n]和Y[m]是否相等
    {
        getAnswer(n-1, m-1);
        printf("%c", X[n]);     //等价于printf("%c", Y[m]);
    }
    else if(C[n][m] == C[n-1][m])
        getAnswer(n-1, m);
    else
        getAnswer(n, m-1);
}


另一种方法是从后面向前面推算,当然得到的答案也肯能不一样,但一定是最长的:

/*  计算LCS,动态规划从后往前推算  */
#include <iostream>
#include <stdio.h>
#include <memory.h>
#define N 1000
char X[N], Y[N];                                //序列X、Y
int C[N][N];                                    //Cij为Xi和Yj的最长公共子序列
int lcs(int n, int m);                          //计算最长公共子序列
void getAnswer(int x, int y, int n, int m);     //构建答案

int main()
{
    int m, n;
    printf("Enter n and m: ");
    while(2 == scanf("%d%d", &n, &m))
    {
        printf("Enter %d elements for X:", n);
        while(getchar() != '\n') continue;      //清除回车符等
        for(int i = 1; i <= n; ++i)
            scanf("%c", &X[i]);
        printf("Enter %d elements for Y:", m);
        while(getchar() != '\n') continue;
        for(int i = 1; i <= m; ++i)
            scanf("%c", &Y[i]);
        printf("MaxLength: %d\nLCS:", lcs(n, m));
        getAnswer(1, 1, n, m);
        printf("\nEnter n and m: ");
    }
    return 0;
}

int lcs(int n, int m)
{
    for(int i = 1; i <= n + 1; ++i)
        C[i][0] = 0;
    for(int i = 1; i <= m + 1; ++i)
        C[0][i] = 0;

    for(int i = n; i >= 1; --i)
    {
        for(int j = m; j >= 1; --j)
        {
            if(X[i] == Y[j])
                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[1][1];
}

void getAnswer(int x, int y, int n, int m)
{
    if(x == n + 1 || y == m + 1)
        return;
    if((C[x][y] == C[x+1][y+1] + 1) && (X[x] == Y[y]))
    {
        printf("%c", X[x]);     //等价于printf("%c", Y[y]);
        getAnswer(x+1, y+1, n, m);
    }
    else if(C[x][y] == C[x+1][y])
        getAnswer(x+1, y, n, m);
    else
        getAnswer(x, y+1, n, m);
}


如有疏漏,请批评指正

0
0
查看评论
发表评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场

动态规划之最长公共子序列问题 C++实现

动态规划之最长公共子序列问题 C++实现原理在之前的文章当中,作者论述了设么事动态规划,这次,我们来看看,如何用动态规划解决最长公共子序列问题。这个问题经常运用在判断两种生物的相似度—-DNA比对上。...
  • liu798675179
  • liu798675179
  • 2016-11-08 12:12
  • 1031

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

1.基本概念       首先需要科普一下,最长公共子序列(longest common sequence)和最长公共子串(longest common substring)不是一回事儿。什么是子序...
  • hrn1216
  • hrn1216
  • 2016-05-29 22:54
  • 41470

使用动态规划解决最长公共子序列问题

一、定义: 给定两个序列X和Y,如果Z既是X的子序列也是Y的子序列,那么我们称Z是X和Y的公共子序列。例如:X={a,b,c,e,d,g,f},Y={b,e,f,g},那么Z={b}、Z={b,e}、...
  • heart_love
  • heart_love
  • 2016-03-04 17:27
  • 2377

最长公共子序列问题(动态规划求解)

问题       求长为m的序列和长为n的序列的最长公共子序列(可以不连续),如ABCBDAB和BDCABA,BCAB和BCBA都是它们的最长公共子序列。在生物学上用来求DNA序列的匹配度。这里我们用...
  • qq_26010491
  • qq_26010491
  • 2016-02-08 10:45
  • 895

算法分析与设计实验 动态规划法 求最长公共子序列

实验目的   加深对动态规划法的算法原理及实现过程的理解,学习用动态规划法解决实际应用中的 最长公共子序列问题。 实验内容   内容: 用动态规划法实现求两序列的最长公共子序列,其比较结果可用于基因比...
  • wyh7280
  • wyh7280
  • 2015-05-07 18:18
  • 1447

Java-LCS最长公共子序列(动态规划实现)

一个序列S任意删除若干个字符得到新序列T,则T称为S的子序列。若两个序列X和Y的公共子序列中,长度最长的那个字序列称为X和Y的最长公共子序列(LCS)。 Xi表示字符串的前i个字符,Yj表示字符串的前...
  • qq_30507287
  • qq_30507287
  • 2016-10-16 14:40
  • 2305

【动态规划】输出所有的最长公共子序列

上篇讲到使用动态规划可以在 θ(mn) 的时间里求出 LCS 的长度,本文将讨论如何输出最长公共子序列。 问题描述:给定两个序列,例如 X = “ABCBDAB”、Y = “BDCABA”,求它们的最...
  • lisong694767315
  • lisong694767315
  • 2014-11-29 15:41
  • 6900

动态规划——最长公共子序列总结

子序列 sub sequence问题,例:最长公共子序列,[LeetCode] Distinct Subsequences(求子序列个数) 子序列和子字符串或者连续子集的不同之处在于,子序列...
  • u014511737
  • u014511737
  • 2015-09-19 10:50
  • 469

Java动态规划 实现最长公共子序列以及最长公共子字符串

动态规划法 经常会遇到复杂问题不能简单地分解成几个子问题,而会分解出一系列的子问题。简单地采用把大问题分解成子问题,并综合子问题的解导出大问题的解的方法,问题求解耗时会按问题规模呈幂级数增加。 ...
  • hanleijun
  • hanleijun
  • 2014-04-15 22:28
  • 749

动态规划基础篇之最长公共子序列问题

一些概念: (1)子序列: 一个序列A = a1,a2,……an,中任意删除若干项,剩余的序列叫做A的一个子序列。也可以认为是从序列A按原顺序保留任意若干项得到的序列。 例如: 对序列 ...
  • lz161530245
  • lz161530245
  • 2017-08-08 21:12
  • 371
    联系我
    交流或者提问请加入QQ群:532232743
    个人资料
    • 访问:169399次
    • 积分:2824
    • 等级:
    • 排名:第14658名
    • 原创:118篇
    • 转载:2篇
    • 译文:0篇
    • 评论:54条
    博客专栏
    最新评论