分类目录:《算法设计与分析》总目录
相关文章:
· 动态规划(一):基础知识
· 动态规划(二):钢条切割
· 动态规划(三):矩阵链乘法
· 动态规划(四):动态规划详解
· 动态规划(五):最长公共子序列


在生物应用中,经常需要比较两个(或多个)不同生物体的DNA。一个DNA串由一串称为碱基的分子组成,碱基有腺嘌呤、鸟嘌呤、胞嘧啶和胸腺嘧啶4种类型。我们用英文单词首字母表示4种碱基,这样就可以将一个DNA串表示为有限集A,C,G,T{A,C,G,T}A,C,G,T上的一个字符串。例如,某种生物的DNA可能为S1=ACCGGTCGAGTGCGCGGAAGCCGGCCGAAS_1= ACCGGTCGAGTGCGCGGAAGCCGGCCGAAS1​=ACCGGTCGAGTGCGCGGAAGCCGGCCGAA,另一种生物的DNA可能为S2=GTCGTTCGGAATGCCGTTGCTCTGTAAAS_2= GTCGTTCGGAATGCCGTTGCTCTGTAAAS2​=GTCGTTCGGAATGCCGTTGCTCTGTAAA。我们比较两个DNA串的一个原因是希望确定它们的“相似度”,作为度量两种生物相近程度的指标。我们可以用很多不同的方式来定义相似度,实际上也确实已经出现了很多相似度的定义。例如,如果一个DNA串是另一个DNA串的子串,那么可以说它们是相似的。但在我们的例子中,S1S_1S1​和S2S_2S2​都不是对方的子串。我们还可以这样来定义相似性:如果将一个串转换为另一个串所需的操作很少,那么可以说两个串是相似的。另一种衡量S1S_1S1​和S2S_2S2​的相似度的方式是:寻找第三个串S3S_3S3​,它的所有碱基也都出现在S1S_1S1​和S2S_2S2​中,且在三个串中出现的顺序都相同,但在S1S_1S1​和S2S_2S2​中不要求连续出现。可以找到的S3S_3S3​越长,就可以认为S1S_1S1​和S2S_2S2​的相似度越高。在我们的例子中,最长的S3S_3S3​为 GTCGTCGGAAGCCGGCCGAAGTCGTCGGAAGCCGGCCGAAGTCGTCGGAAGCCGGCCGAA。

我们将最后一种相似度的概念命名为最长公共子序列问题。一个给定序列的子序列,就是将给定序列中零个或多个元素去掉之后得到的结果。其形式化定义如下:给定一个序列X=<x1,x2,⋯,xn>X=<x_1, x_2, \cdots, x_n>X=<x1​,x2​,⋯,xn​>,另一个序列Z=<z1,z2,⋯,zm>Z=<z_1, z_2, \cdots, z_m>Z=<z1​,z2​,⋯,zm​>满足如下条件时称为XXX的子序列,即存在一个严格递增的XXX的下标序列<i1,i2,⋯,ik><i_1, i_2, \cdots, i_k><i1​,i2​,⋯,ik​>,对所有j=1,2,⋯,kj=1, 2, \cdots, kj=1,2,⋯,k满足Xik=ZjX_{i_k}=Z_jXik​​=Zj​。例如,Z=<B,C,E>Z=<B, C, E>Z=<B,C,E>是X=<A,B,C,D,E,F,G>X=<A, B, C, D, E, F, G>X=<A,B,C,D,E,F,G>的子序列,对应的下标序列为2,3,52, 3, 52,3,5。

给定两个序列XXX和YYY,如果ZZZ既是XXX的子序列,也是YYY的子序列,我们称它是XXX和YYY的公共子序列。例如,如果X=<A,B,C,D,E,F,G>X=<A, B, C, D, E, F, G>X=<A,B,C,D,E,F,G>,X=<B,B,D,C,D,E,G>X=<B, B, D, C, D, E, G>X=<B,B,D,C,D,E,G>,那么序列Z=<B,C,>Z=<B, C,>Z=<B,C,>就是XXX和YYY的公共子序列。但它不是XXX和YYY的最长公共子序列(LCS),因为它长度为2,而<B,C,E><B, C, E><B,C,E>也是XXX和YYY的公共子序列,其长度为3。<B,C,D,E,G><B, C, D, E, G><B,C,D,E,G>是XXX和YYY的最长公共子序列。最长公共子序列问题( longest-common-subsequence problem)说的是给定两个序列X=<x1,x2,⋯,xm>X=<x_1, x_2, \cdots, x_m>X=<x1​,x2​,⋯,xm​>和Y=<x1,x2,⋯,xn>Y=<x_1, x_2, \cdots, x_n>Y=<x1​,x2​,⋯,xn​>,求XXX和YYY长度最长的公共子序列。下面我们讲如何用动态规划方法高效地求解LCS问题:

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

如果用暴力搜索方法求解LCS问题,就要穷举XXX的所有子序列,对每个子序列检查它是否也是YYY的子序列,记录找到的最长子序列。XXX的每个子序列对应XXX的下标集合1,2,⋯,m{1, 2, \cdots, m}1,2,⋯,m的一个子集,所以XXX有2m2^m2m个子序列,因此暴力方法的运行时间为指数阶,对较长的序列是不实用的。

根据定义,我们可以很直观地得到最长公共子序列的最优质子结构性质:

令X=<x1,x2,⋯,xm>X=<x_1, x_2, \cdots, x_m>X=<x1​,x2​,⋯,xm​>和Y=<x1,x2,⋯,xn>Y=<x_1, x_2, \cdots, x_n>Y=<x1​,x2​,⋯,xn​>为两个序列,Z=<z1,z2,⋯,zk>Z=<z_1, z_2, \cdots, z_k>Z=<z1​,z2​,⋯,zk​>为XXX和YYY的任意LCS。

  1. 如果Xm=YnX_m=Y_nXm​=Yn​,则Zk=Xm=YnZ_k=X_m=Y_nZk​=Xm​=Yn​且Zk−1Z_{k-1}Zk−1​是Xm−1X_{m-1}Xm−1​和Yn−1Y_{n-1}Yn−1​的一个LCS。
  2. 如果Xm≠YnX_m≠Y_nXm​​=Yn​,且Zk≠XmZ_k≠X_mZk​​=Xm​意味着ZZZ是Xm−1X_{m-1}Xm−1​和YYY的一个LCS。
  3. 如果Xm≠YnX_m≠Y_nXm​​=Yn​,且Zk≠YnZ_k≠Y_nZk​​=Yn​意味着ZZZ是XXX和Yn−1Y_{n-1}Yn−1​的一个LCS。

如上文所示,LCS问题具有最优子结构性质。我们将看到,子问题的自然分类对应两个输入序列的“前缀”对。前缀的严谨定义如下:给定一个序列X=<x1,x2,⋯,xm>X=<x_1, x_2, \cdots, x_m>X=<x1​,x2​,⋯,xm​>,对i=1,2,⋯,mi = 1, 2, \cdots, mi=1,2,⋯,m,定义XXX的第i前缀为X=<x1,x2,⋯,xi>X=<x_1, x_2, \cdots, x_i>X=<x1​,x2​,⋯,xi​>。例如,若X=<A,B,C,D,E,F,G>X=<A, B, C, D, E, F, G>X=<A,B,C,D,E,F,G>,则X3=<A,B,C>X_3=<A, B, C>X3​=<A,B,C>,X0X_0X0​为空串。所以,两个序列的LCS包含两个序列的前缀的LCS。因此,LCS问题具有最优子结构性质。

步骤2:一个递归解

有步骤1可以得出,在求X=<x1,x2,⋯,xm>X=<x_1, x_2, \cdots, x_m>X=<x1​,x2​,⋯,xm​>和Y=<x1,x2,⋯,xn>Y=<x_1, x_2, \cdots, x_n>Y=<x1​,x2​,⋯,xn​>的一个LCS时,我们需要求解一个或两个子问题。如果Xm=YnX_m=Y_nXm​=Yn​,我们应该求解Xm−1X_{m-1}Xm−1​和Yn−1Y_{n-1}Yn−1​的一个LCS。将Xm=YnX_m=Y_nXm​=Yn​追加到这个LCS的末尾,就得到X和Y的一个LCS。如果Xm≠YnX_m≠Y_nXm​​=Yn​,我们必须求解两个子问题:求Xm−1X_{m-1}Xm−1​和YYY的一个LCS与XXX和Yn−1Y_{n-1}Yn−1​的一个LCS。两个LCS较长者即为XXX和YYY的一个LCS。由于这些情况覆盖了所有可能性,因此我们知道必然有一个子问题的最优解出现在XXX和YYY的LCS中。

我们可以很容易看出LCS问题的重叠子问题性质。为了求XXX和YYY的一个LCS,我们可能需要求Xm−1X_{m-1}Xm−1​和YYY的一个LCS与XXX和Yn−1Y_{n-1}Yn−1​的一个LCS。但是这几个子问题都包含求解Xm−1X_{m-1}Xm−1​和Yn−1Y_{n-1}Yn−1​的ICS的子子问题。很多其他子问题也都共享子子问题。

与矩阵链乘法问题相似,设计LCS问题的递归算法首先要建立最优解的递归式。我们定义c[i,j]c[i, j]c[i,j]表示XiX_iXi​和YjY_jYj​的LCS的长度。如果i=0i=0i=0或j=0j=0j=0,即一个序列长度为0,那么LCS的长度为0。根据ICS问题的最优子结构性质,可得如下公式:

观察到在递归公式中,我们通过限制条件限定了需要求解哪些子问题。当Xi=YjX_i=Y_jXi​=Yj​时,我们可以而且应该求解子问题:Xi−1X_i-1Xi​−1和Yj−1Y_{j-1}Yj−1​的一个LCS。否则,应该求解两个子问题:XiX_iXi​和Yj−1Y_{j-1}Yj−1​的一个LCS及Xi−1X_i-1Xi​−1和YjY_jYj​的一个LCS。在之前讨论过的钢条切割问题和矩阵链乘法问题的动态规划算法中,根据问题的条件,我们没有排除任何子问题。

步骤3:计算LCS的长度

根据上面的分析,我们可以很容易地写出一个指数时间的递归算法来计算两个序列的LCS的长度。但是,由于LCS问题只有mnmnmn个不同的子问题,我们可以用动态规划方法自底向上地计算。

过程lce_length(X, Y)接受两个序列X=[x1,x2,⋯,xm]X=[x_1, x_2, \cdots, x_m]X=[x1​,x2​,⋯,xm​]和Y=[x1,x2,⋯,xn]Y=[x_1, x_2, \cdots, x_n]Y=[x1​,x2​,⋯,xn​]为输入。它将c[i,j]c[i, j]c[i,j]的值保存在表c[0⋯m,0⋯n]c[0\cdots m, 0\cdots n]c[0⋯m,0⋯n]中,并按行主次序计算表项(即首先由左至右计算c的第一行,然后计算第二行,依此类推)。过程还维护一个表b[i,j]b[i, j]b[i,j],帮助构造最优解。b[i,j]b[i, j]b[i,j]指向的表项对应计算c[i,j]c[i, j]c[i,j]时所选择的子问题最优解。过程返回表bbb和表ccc。

import numpy as np
def lcs_length(X, Y):m = len(X)n = len(Y)b = np.zeros([m + 1, n + 1]) c = np.zeros([m + 1, n + 1])for i in range(1, m + 1):for j in range(1, n + 1):if X[i - 1] == Y[j - 1]:c[i, j] = c[i - 1, j - 1] + 1b[i, j] = '1'elif c[i - 1, j] >= c[i, j -1]:c[i, j] = c[i - 1, j]b[i, j] = '2'else:c[i, j] = c[i, j -1]b[i, j] = '3'return c, b

下图显示了ce_length(X, Y)对输入X = 'ABCBDAB'Y = 'BDCABA',过程的运行时间为O(mn)O(mn)O(mn)。

其中,b中的111表示↖↖↖、222表示↑↑↑、333表示←←←。

步骤4:构造LCS

我们可以用print_lcs(X, b, m, n)返回的表b快速构造X=<x1,x2,⋯,xm>X=<x_1, x_2, \cdots, x_m>X=<x1​,x2​,⋯,xm​>和Y=<x1,x2,⋯,xn>Y=<x_1, x_2, \cdots, x_n>Y=<x1​,x2​,⋯,xn​>的LCS,只需简单地从b[m,n]b[m, n]b[m,n]开始,并按箭头方向追踪下去即可。

def print_lcs(X, b, m, n):if m == 0 or n == 0:return ''if b[m, n] == 1:print_lcs(X, b, m - 1, n - 1)print(X[m - 1])elif b[m, n] == 2:print_lcs(X, b, m - 1, n)else:print_lcs(X, b, m, n - 1)

算法设计与分析——动态规划(五):最长公共子序列相关推荐

  1. javascript写算法(一) 动态规划:最长公共子序列

    csdn是疯了吗,右下角的广告是一个人站在猪屁股后面打它...看了一下居然是baidu算出来的广告嵌在了iframe里,fixed to viewport,真是够了.最近还频频出现的广告是一个光头男子 ...

  2. PHP第五周答案,算法设计与分析第五周作业——Word Ladder

    算法设计与分析第五周作业--Word Ladder 上周找了一道深度搜索优先搜索的算法题来做,于是这周就选了一道广度优先搜索算法题来试试手. 本周所选题目:原题目链接 题目详情 题目大意:给出一个字符 ...

  3. 算法设计与分析-----动态规划

    算法设计与分析-----动态规划(c语言) 一.动态规划 1.定义 2.动态规划问题的解法 3.动态规划求解的基本步骤 4.动态规划与其他方法的比较 5.求解整数拆分问题 6.求解最大连续子序列和问题 ...

  4. 计算机算法设计与分析第五章思维导图知识点总结 ( 初稿 )

    复习链接 计算机算法设计与分析第一章思维导图 计算机算法设计与分析第二章思维导图&&知识点总结 计算机算法设计与分析第三章思维导图&&知识点总结 计算机算法设计与分析第 ...

  5. 动态规划算法下的序列问题:最长公共子序列问题和最大子段和问题

    本篇主要介绍最长公共子序列问题和最大子段和问题 1.最长公共子序列问题 什么是最长公共子序列 给定一个序列X=<x1,x2,x3,x4-,xm>,另一个序列Z=<z1,z2,z3,z ...

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

    动态规划算法的基本要素: 1)最优子结构 当问题的最优解包含了其子问题的最优解时,称该问题具有最优子结构性质.问题的最优子结构性质提供了该问题可用动态规划算法求解的最重要线索. 在动态规划算法中,利用 ...

  7. 动态规划解最长公共子序列(LCS)(附详细填表过程)

    目录 相关概念 子序列形式化定义: 公共子序列定义: 最长公共子序列(以下简称LCS): 方法 蛮力法求解最长公共子序列: 动态规划求解最长公共子序列: 分析规律: 做法: 伪代码: 下面演示下c数组 ...

  8. 动态规划之最长公共子序列(LCS)

    最长公共子序列(LCS,Longest Common Subsequence).其定义是,一个序列 S ,如果分别是两个或多个已知序列的子序列,且是所有符合此条件序列中最长的,则 S 称为已知序列的最 ...

  9. 动态规划解决最长公共子序列

    动态规划解决最长公共子序列 问题描述 给定两个序列,例如 X = "ABCBDAB".Y = "BDCABA",求它们的最长公共子序列的长度. 递归关系 c[i ...

最新文章

  1. 渗透测试中的文件传输技巧
  2. python编程语法-Python基础及语法(十三)
  3. 【DBMS 数据库管理系统】数据仓库中 数据追加 ( 时标方法 | DELTA 文件法 | 前后映像文件法 | 日志文件法 )
  4. C# 调用C++ dll
  5. 指针-数组传参,指针传参
  6. 【渝粤题库】广东开放大学 社会学基础 形成性考核
  7. 【转载】浅谈React编程思想
  8. php随笔11-Thinkphp常用系统配置大全
  9. PHP学习之会话控制session、cookie
  10. Linux的重要子目录
  11. “叔叔,你来监考了!”
  12. 热烈祝贺新疆.Net俱乐部博客开通——天下博客开通
  13. Exchange使用正常的恢复无法恢复的问题
  14. SQL常用替换字符串值的5种方法
  15. 11 Steps Attackers Took to Crack Target
  16. 【人工智能】NIPS2019 | 2019NIPS论文 | NeurIPS2019最新更新论文~持续更新| NIPS2019百度云下载
  17. Mac和Windows中常见中文字体的英文名称
  18. 修改vscode图标
  19. 用Python模拟QQ界面之QQ登录界面的奥秘
  20. 系统内存占用率高导致电脑卡顿的解决方案

热门文章

  1. 将个人java web网站发布至公网#内网穿透#花生壳#手把手教程
  2. “狼来了”貌似对安全有用
  3. (6K-10K外派到盛大).NET/PHP/Java/Web前端/Linux C++开发
  4. 梦幻显示无法连接服务器列表,梦幻怪兽无法连接服务器是什么原因
  5. 优漫动游中国有哪些著名平面设计师呢?
  6. Redux之useSelector、useDispatch
  7. python项目之欢天喜地接元宝
  8. Python学习笔记:第九站 一串连一串
  9. 整车出厂 合格证 V3.0
  10. MySQL 交集查询函数实现