首先定义一个给定序列的子序列,就是将给定序列中零个或多个元素去掉之后得到的结果,其形式化定义如下:给定一个序列X = <x1,x,..., xm>,另一个序列Z =<z1,z,..., zk> 满足如下条件时称为X的子序列,即存在一个严格递增的X的下标序列<i1,i,..., ik>,对于所有j = 1,2,...,k,满足xij = zj,例如,Z=<B,C,D,B>是X=<A,B,C,B,D,A,B>的子序列,对应的下标序列为<2,3,5,7>。给定两个序列X和Y,如果Z是X的子序列,也是Y的子序列,则称它是X和Y的公共子序列。

最长公共子序列问题(longest-common-subsequence problem)可用动态规划方法高效地求解。

步骤1:刻画最长公共子序列的特征

LCS问题具有 最优子结构性质。子问题的自然分类对应两个输入序列的“前缀"对。"前缀"的定义如下:给定一个序列X = <x1,x,..., xm>,对于i = 0,1,...,m,定义X的第i前缀为Xi = <x1,x,..., xi>。例如,若 X = <A,B,C,B,D,A,B>,则 X4 = <A,B,C,B>,X0为空串。

令X = <x1,x,..., xm>和Y = <y1,y,..., yn> 为两个序列,Z =<z1,z,..., zk>为X和Y的任意LCS。

  1. 如果Xm = Yn,则 Zk =  Xm = Yn且Zk-1 是Xm-1和Yn-1的一个LCS。
  2. 如果 Xm ≠ Yn,那么Zk ≠  Xm意味着Z是Xm-1和Y的一个LCS。
  3. 如果 Xm ≠ Yn,那么Zk ≠  Yn意味着Z是X和Yn-1的一个LCS。

步骤2:一个递归解

很容易看出LCS问题的重叠子问题性质。为了求X和Y的一个LCS,我们可能需要求X和Yn-1的一个LCS及Xm-1和Y的一个LCS。但是这几个子问题都包含求解Xm-1和Yn-1的LCS的子子问题。我们定义c[i,j]表示Xi和Yj的LCS的长度。如果i= 0 或j = 0,即一个序列长度为0,那么LCS的长度为0,根据LCS问题的最优子结构性质,可得如下公式:

步骤3:计算LCS的长度

过程LCS-LENGTH接受两个序列X = <x1,x,..., xm>和Y = <y1,y,..., yn>为输入。它将c[i,j]的值保存在表c[0...m,0...n]中,过程还维护一个表b[1...m,1...n],帮助构造最优解。b[i,j]指向的表项对应计算c[i,j]时所选择的子问题最优解。过程返回表b和表c,c[m,n]保存了X和Y的长度。

//伪代码
LCS-LENGTH(X,Y)
m = X.length
n = Y.length
let b[1..m,1..n] and c[0..m,0..n]be new tables
for i = 1 to m
    c[i,0] = 0
for j = 0 to n
    c[0,j] = 0
for i = 1 to m
    for j = 1 to n
    if xi == yi
      c[i,j] = c[i-1,j-1] + 1
      b[i,j] = "\"

    else if c[i-1,j] ≥ c[i,j-1]

      c[i,j] = c[i-1,j]

      b[i,j] = "|"

    else

      c[i,j] = c[i,j-1]

      b[i,j] = "—"

return c and b

下图显示了LCS-LENGTH对输入序列X= <A,B,C,B,D,A,B>和Y=<B,D,C,A,B,A>生成的结果。过程的运行时间为O(mn)。

步骤4:构造LCS

利用LCS-LENGTH返回的表b快速构造X和Y的LCS,只需简单地从b[m,n]开始,并按箭头方向追踪下去即可。挡在表项b[i,j]中遇到一个”\"时,意味着xi=yi是LCS的一个元素。下面的递归过程会按正确的顺序打印出X和Y的一个LCS。对它的起始调用为PRINT-LCS(b,X,X.length,Y.length)。

PRINT-LCS(b,X,i,j)

  if == 0 or j==0

    return

  if b[i,j] == "\"

    PRINT-LCS(b,X,i-1,j-1)

    print xi

  else if b[i,j] == "|"

    PRINT-LCS(b,X,i-1,j)

  else

    PRINT-LCS(b,X,i,j-1)

实现:

 void lcsLength(string x,string y, vector< vector<int>> &c, vector< vector<char>> &b)
{
int m = x.size();
int n = y.size();
c.resize(m+);
b.resize(m+);
for(int i = ; i < c.size(); ++i)
c[i].resize(n+);
for(int i = ; i < b.size(); ++i)
b[i].resize(n+); for(int i = ; i <= m; ++i){
for(int j = ; j <= n; ++j){
if(x[i-] == y[j-]){
c[i][j] = c[i-][j-]+;
b[i][j] = 'c';
}else if(c[i-][j] >= c[i][j-]){
c[i][j] = c[i-][j];
b[i][j] ='u';
}else{
c[i][j] = c[i][j-];
b[i][j] = 'l';
}
}
}
}
 void print_lcs(vector< vector<char>> &b,string x, int i, int j)
{
if(i == || j == )
return;
if(b[i][j] == 'c'){
print_lcs(b,x,i-,j-);
cout << x[i-];
}else if(b[i][j] == 'u')
print_lcs(b,x,i-,j);
else
print_lcs(b,x,i,j-);
}

例子:

 int main()
{
string x = "ABCBDAB";
string y = "BDCABA";
vector< vector<int>> c;
vector< vector<char>> b; lcsLength(x,y,c,b);
print_lcs(b,x,x.size(),y.size());
}

输出:

算法导论-动态规划(最长公共子序列问题LCS)-C++实现的更多相关文章

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

    最长公共子序列也是动态规划中的一个经典问题. 有两个字符串 S1 和 S2,求一个最长公共子串,即求字符串 S3,它同时为 S1 和 S2 的子串,且要求它的长度最长,并确定这个长度.这个问题被我们称 ...

  2. 动态规划———最长公共子序列(LCS)

    最长公共子序列+sdutoj2080改编: http://acm.sdut.edu.cn/onlinejudge2/index.php/Home/Contest/contestproblem/cid/ ...

  3. 动态规划——最长公共子序列(LCS)

    /** * @brief longest common subsequence(LCS) * @author An * @data 2013.8.26 **/ #include <iostrea ...

  4. 动态规划经典——最长公共子序列问题 (LCS)和最长公共子串问题

    一.最长公共子序列问题(LCS问题) 给定两个字符串A和B,长度分别为m和n,要求找出它们最长的公共子序列,并返回其长度.例如: A = "HelloWorld"    B = & ...

  5. 程序员的算法课(6)-最长公共子序列(LCS)

    版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/m0_37609579/article/de ...

  6. 动态规划——最长公共子序列&&最长公共子串

      最长公共子序列(LCS)是一类典型的动归问题. 问题 给定两个序列(整数序列或者字符串)A和B,序列的子序列定义为从序列中按照索引单调增加的顺序取出若干个元素得到的新的序列,比如从序列A中取出 A ...

  7. 动态规划法(十)最长公共子序列(LCS)问题

    问题介绍   给定一个序列\(X=<x_1,x_2,....,x_m>\),另一个序列\(Z=<z_1,z_2,....,z_k>\)满足如下条件时称为X的子序列:存在一个严格 ...

  8. 【Luogu P1439】最长公共子序列(LCS)

    Luogu P1439 令f[i][j]表示a的前i个元素与b的前j个元素的最长公共子序列 可以得到状态转移方程: if (a[i]==b[j]) dp[i][j]=dp[i-1][j-1]+1; d ...

  9. 最长公共子序列(LCS)、最长递增子序列(LIS)、最长递增公共子序列(LICS)

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

随机推荐

  1. PowerDesign不让name和code联动

    当name和code一样时,修改name的话,code的值将跟着变动,很不方便.如果能让code不随着name编码就好了. PowerDesign中的选项菜单,在[Tool]-->[Genera ...

  2. ibernate学习笔记5---实体类或属性名与数据库关键字冲突、hql命名参数、hql实现通用分页

    一.实体类或属性名与数据库关键字冲突问题1.实体类名与数据库中的关键字冲突比如:实体表User与oracle中的系统表冲突解决方式1:在xml中添加table属性,指定表名,使其不与name默认相等 ...

  3. POJ 3107 Godfather (树形dp)

    题目链接 虽然题目不难,但是1A还是很爽, 只是刚开始理解错题意了,想了好久. 还有据说这个题用vector会超时,看了以后还是用邻接吧. 题意: 给一颗树,保证是一颗树,求去掉一个点以后的联通块里节 ...

  4. hdu1050(贪心)

    囧 . 想了好久,一开始想的是一个连通图怎样用黑白两色染色,想了各种算法发现都不好做,然后灵机一动这不是网路流吗,然后想怎么建图,如果转换成网络流这题就好做了,建图加个二分应该就可以解决了,最后又发现 ...

  5. c#调用系统资源大集合-3

    public static void 打开控制面板多媒体属性音频() { Process.Start("rundll32.exe", " shell32.dll,Cont ...

  6. POJ 1037 (计数 + DP) 一个美妙的栅栏

    这道题总算勉勉强强看懂了,DP和计数都很不好想 DP部分: 称i根木棒的合法方案集合为S(i),第二根木棒比第一根长的方案称作UP方案,反之叫做DOWN方案 C[i][k][DOWN] 是S(i)中以 ...

  7. WEBUS2.0 In Action - 搜索操作指南 - (4)

    上一篇:WEBUS2.0 In Action - 搜索操作指南(3) 6. 搜索多个索引 为了提升性能, 我们可以从多个索引同时进行搜索, Webus.Search.MultiSearcher提供了相 ...

  8. SQL语句方法语法总结(三)

    1.时间相关的操作 月份.星期.日期.时间格式转换.第几周 ,'2014-4-1') as '时间间隔', --在所给时间上加上时间间隔,转换成DATETIME DATEDIFF(DAY,'2014- ...

  9. 浅析Android中的消息机制(转)

    在分析Android消息机制之前,我们先来看一段代码: public class MainActivity extends Activity implements View.OnClickListen ...

  10. 2015-10-15 晴 APUE GIT

    昨天看完了tcp/ip协议,快速的看完了这些内容,而且这里面的东西以前很熟悉,今天只是大概一看就想起以前用到的,什么时候用到的,以前在企智通上出过什么问题,后来如何解决的.我都有点佩服我的记忆.网络协 ...