算法导论--动态规划(最长公共子序列)

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

给定两个序列X=?x1,x2,x3...xm?和Y=?y1,y2,y3...xn?,求X和Y的最长公共子序列。

例如:X=?A,B,C,B,D,A,B?,和Y=?B,D,C,A,B,A?,的最长公共子序列为?B,C,B,A?,长度为4;

对于此问题,可以采用暴力求解的方式来比对,即穷举出X的所有子序列,用每个子序列与y做一 一比较。假如X序列共有m个元素,对每个元素可以决定选或不选,则X的子序列个数共有2m个,可见与长度m呈指数阶,这种方法效率会很低。

动态规划

前几篇博文已介绍了采用动态规划的方法解决装配线问题及钢铁切割问题,它们都满足了两个条件:1.具有最优子结构的特征 2.子问题重叠。

最长公共子序列的特征

首先定义前缀Xi的含义为序列X=?x1,x2,x3...xn?的前i项组成的子序列,即Xi=?x1,x2,x3...xi?(0≤i≤n),X0为空串。

则有以下定理:



假如序列X=?x1,x2,x3...xm?,Y=?y1,y2,y3...xn?的一个LCS为Z=?z1,z2,z3...zk?

1.如果xm=yn,则zk=xm=yn,且Zk?1是Xm?1和Yn?1的一个LCS

2.如果xm≠yn,那么zk≠xm,则Z是Xm?1和Y的一个LCS

2.如果xm≠yn,那么zk≠yn,则Z是X和Yn?1的一个LCS



上述定理即为要求得X和Y的LCS,可以根据xm与yn的关系,如果xm=yn,则问题成了求Xm?1与Yn?1的LCS;如果xm≠yn,则问题变成求两对子序列的LCS, Xm?1,Yn的LCS及Xm,Yn?1的LCS,而且具有重叠子问题的性质都需要求Xm?1,Yn?1的LCS。

由以上分析:令c[i][j]为表示序列Xi和Yj的LCS的长度,则有:

c[i][j]=0,若i=0或者j=0

c[i][j]=c[i?1][j?1]+1,若i,j>0,且xi=yj

c[i][j]=max(c[i][j?1],c[i?1][j]),若i,j>0,且xi≠yj

计算LCS的长度

用数组c[m][n]保存子序列Xm,Yn的LCS的长度,由上分析,c[i][j]的值取决于c[i-1][j-1]、c[i][j-1]、c[i-1][j],可以采用自底向上的方法向上逐次计算,这样每个子问题只需要计算一次;用b[i][j]保存计算c[i][j]时,所选的子问题;

X=?A,B,C,B,D,A,B?,和Y=?B,D,C,A,B,A?

从上到下,从做到右:复杂度为Θ(m?n)

构造LCS

1.可以采用b[i][j]中记录的值,依次倒序输出,从b[7][6]开始倒序输出。也可以用递归的方法,从底层开始,正序输出;

2.不用b[i][j],即改进版,由于c[i][j]是由c[i-1][j-1]、c[i-1][j]、c[i][j-1],可以根据它们的关系,判断出方向;运行时间都为O(m+n);

例程

求X=?A,B,C,B,D,A,B?,和Y=?B,D,C,A,B,A?的一个LCS

/************************************************************************
CSDN 勿在浮沙筑高台
http://blog.csdn.net/luoshixian099
算法导论--动态规划(最长公共子序列)
2015年6月5日
main.cpp
************************************************************************/
#include<iostream>
using namespace std;
int b[8][7]={0},c[8][7]={0};
void Lcs_Length(char *X,char *Y) // 遍历序列X和Y,自底向上计算出c[i][j]
{
    for(int i=1;i<=7;i++)
        for(int j=1;j<=6;j++)
        {
            if(X[i] == Y[j])
            {
                c[i][j] = c[i-1][j-1]+1;
                b[i][j] = 2;              //左上方
            }
            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] = 3;       //左方
            }
        }
}
void Print_Lcs(int b[][7],char *X,int i,int j)  //采用b[i][j]递归输出LCS
{
    if(i == 0 || j == 0)
        return ;
    if(b[i][j] == 2)
    {
        Print_Lcs(b,X,i-1,j-1);
        cout<<"  "<<X[i];
    }
    else if(b[i][j] == 1)
        Print_Lcs(b,X,i-1,j);
    else
        Print_Lcs(b,X,i,j-1);
}
void Print_Lcs(char *X,char *Y,int i,int j)//不用b[i][j],递归输出LCS
{

    if(i == 0 || j == 0)
        return ;
    if(X[i] == Y[j])
    {
        Print_Lcs(X,Y,i-1,j-1);
        cout<<"  "<<X[i];
    }
    else if(c[i-1][j] >= c[i][j-1])
        Print_Lcs(X,Y,i-1,j);
    else
        Print_Lcs(X,Y,i,j-1);
}
int main()
{
    char X[]={‘ ‘,‘A‘,‘B‘,‘C‘,‘B‘,‘D‘,‘A‘,‘B‘};
    char Y[]={‘ ‘,‘B‘,‘D‘,‘C‘,‘A‘,‘B‘,‘A‘};
    Lcs_Length(X,Y);
    for(int i=0;i<8;i++)   //输出数组内的值
    {
        for(int j=0;j<7;j++)
            cout<<"  "<<c[i][j];
        cout<<endl;
    }
    cout<<endl;
    Print_Lcs(b,X,7,6);  //输出LCS
    cout<<endl;
    Print_Lcs(X,Y,7,6);  //改进的版本
    cout<<endl;
    return 0;
}
时间: 2025-01-02 15:53:13

算法导论--动态规划(最长公共子序列)的相关文章

动态规划算法之:最长公共子序列 & 最长公共子串(LCS)

1.先科普下最长公共子序列 & 最长公共子串的区别: 找两个字符串的最长公共子串,这个子串要求在原字符串中是连续的.而最长公共子序列则并不要求连续. 2.最长公共子串 其实这是一个序贯决策问题,可以用动态规划来求解.我们采用一个二维矩阵来记录中间的结果.这个二维矩阵怎么构造呢?直接举个例子吧:"bab"和"caba"(当然我们现在一眼就可以看出来最长公共子串是"ba"或"ab") b a b c 0 0 0 a 0 1

算法导论之动态规划(最长公共子序列和最优二叉查找树)

动态规划师通过组合子问题的解而解决整个问题,将问题划分成子问题,递归地求解各子问题,然后合并子问题的解而得到原问题的解.和分治算法思想一致,不同的是分治算法适合独立的子问题,而对于非独立的子问题,即各子问题中包含公共的子子问题,若采用分治法会重复求解,动态规划将子问题结果保存在一张表中,避免重复子问题重复求解. 动态规划在多值中选择一个最优解,其算法设计一般分为4个步骤:描述最优解的结构:递归定义最优解的值:按自底向上的方式计算最优解的值:由计算出的结果构造一个最优解. 1)装配线调度 求解最快

《算法导论》读书笔记之动态规划—最长公共子序列 &amp; 最长公共子串(LCS)

From:http://my.oschina.net/leejun2005/blog/117167 1.先科普下最长公共子序列 & 最长公共子串的区别: 找两个字符串的最长公共子串,这个子串要求在原字符串中是连续的.而最长公共子序列则并不要求连续. 2.最长公共子串 其实这是一个序贯决策问题,可以用动态规划来求解.我们采用一个二维矩阵来记录中间的结果.这个二维矩阵怎么构造呢?直接举个例子吧:"bab"和"caba"(当然我们现在一眼就可以看出来最长公共子串是

算法系列笔记6(动态规划—最长公共子序列/串lcs)

子序列要求元素顺序一致就可以了,而字串必须是连续的.如ABCBDAB与BDCABA两个字符串,最长公共子序列有BCBA.BDAB和BCAB, 而最长公共字串只有AB和BD<连续>.当然这里的求解只求一个,但通常是这样直接说求最长公共子串,子序列,准确的应该是之一. 最长公共子序列 法一:穷举法 检查字符串x所有字序列,共有2^m个,检查它是否在y字符串中出现,每个需要O(n),时间复杂度为指数级的. 法二:动态规划(DP) 将两个字符串x[1-m]和y[1-n]放在x轴和y轴方向上便得到一个二

算法 动态规划 ------最长公共子序列

这篇博客是我 听完这位老师讲完课的 记录.https://www.youtube.com/watch?v=mgDUoITB24I&t=645s    还有我个人的理解,如果有错误或者有争议的地方,欢迎留言.谢谢大家~ 子序列  就是说 从原有列表中按照出现先后顺序从中选择部分 元素 组成的新的列表. 最长公共子序列的含义:  选择两个列表中公共的子序列的最大长度,这个子序列即为最长公共子序列. 用蛮力的方法求解,对于长度为 M的 列表,它所有的子序列的个数有  2^m  ,每个元素有两种情况,被

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

最长公共子序列也是动态规划中的一个经典问题. 有两个字符串 S1 和 S2,求一个最长公共子串,即求字符串 S3,它同时为 S1 和 S2 的子串,且要求它的长度最长,并确定这个长度.这个问题被我们称为 最长公共子序列问题. 与求最长递增子序列一样,我们首先将原问题分割成一些子问题,我们用 dp[i][j]表示 S1 中前 i 个字符与 S2 中前 j 个字符分别组成的两个前缀字符串的最 长公共子串长度. 显然的,当 i. j 较小时我们可以直接得出答案,如 dp[0][j]必 等于 0.那么,

【算法】 求最长公共子序列

最长公共子序列 算法这玩意儿我完全是外行,因为从头开始就不是这个专业的再加上从小就对逻辑性强的东西苦手..所以一直没什么机会也没什么兴趣学.去年刚开始学习了python的那段时间曾经碰到过几个算法比较高级的问题,当时觉得果然这不是我的能力能驾驭的了的..总之是先记录了下来,但是对于算法这块将来的拓展和进一步学习,其实我挺没信心的 = = 问题:最长公共子序列问题(Longest Common Sequence) 子序列是指一个字符串抽掉0到若干个字符后剩下的字符串,抽取的字符不一定相邻也不一定有

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

一些概念: (1)子序列: 一个序列A = a1,a2,……an,中任意删除若干项,剩余的序列叫做A的一个子序列.也可以认为是从序列A按原顺序保留任意若干项得到的序列. 例如: 对序列 1,3,5,4,2,6,8,7来说,序列3,4,8,7 是它的一个子序列.对于一个长度为n的序列,它一共有2^n 个子序列,有(2^n – 1)个非空子序列. 请注意:子序列不是子集,它和原始序列的元素顺序是相关的. (2)公共子序列 : 顾名思义,如果序列C既是序列A的子序列,同时也是序列B的子序列,则称它为序

算法设计 - LCS 最长公共子序列&amp;&amp;最长公共子串 &amp;&amp;LIS 最长递增子序列

出处 http://segmentfault.com/blog/exploring/ 本章讲解:1. LCS(最长公共子序列)O(n^2)的时间复杂度,O(n^2)的空间复杂度:2. 与之类似但不同的最长公共子串方法.最长公共子串用动态规划可实现O(n^2)的时间复杂度,O(n^2)的空间复杂度:还可以进一步优化,用后缀数组的方法优化成线性时间O(nlogn):空间也可以用其他方法优化成线性.3.LIS(最长递增序列)DP方法可实现O(n^2)的时间复杂度,进一步优化最佳可达到O(nlogn)

动态规划-最长公共子序列

(1).问题描述:给出2个序列,x是从1到m,y是从1到n,找出x和y的最长公共子序列? x:A B C B D A B y:B D C A B A 则:最长公共子序列长度为4,BDAB BCAB BCBA均为LCS(最长公共子序列): 模型实现图: (2).问题解决 代码实现了最长公共子序列的长度 #include<stdio.h> #define N    10 int LCS(int *a, int count1, int *b, int count2); int LCS(int *a,