这个问题很有意思,在生物应用中,经常需要比较两个(或多个)不同生物体的DNA片段。例如,某种生物的DNA可能为S1 = ACCGGTCGAGTGCGCGGAAGCCGGCCGAA,S2 = GTCGTTCGGAATGCCGTTGCTCTGTAAA。比较两个DNA串是想要确定它们之间的“相识度”,作为度量两种生物相近程度的指标。LCS就是寻找第三个串S3,S3满足定义:给定一个序列X = <x1,x2,...,xn>,另一个序列Y = <y1,y2,...,ym>即存在一个严格递增的X的下标序列<i1,i2,...,im>,对所有j = 1,2,...,m,满足xij = zj。例如,Z = BCDB是ABCBDAB的子序列,对应的下标为[2,3,5,7]。

  如果用暴力求解则很容易就达到指数阶的运行时间,所以显然需要优化。通过分析发现LCS问题具有最优子结构性质,于是,考虑用动态规划是一个非常不错的方案。

  显然早就有许多人对此类问题做过很多工作,不管是理论上还是实际测试,都证明动态规划可以解决这一类型的问题,有时候甚至是最优方案。如何熟练使用动态规划的一大难点在于分析问题,要理解如何使用动态规划来解决问题,首先分析问题是否具有最优子结构是重要的,然后就是通过分析问题得到递归公式,只要得到了递归公式,我们就可以使用动态规划了。

  LCS问题也不例外,经过分析和查资料,我们知道了LCS得最优子结构定理:

  令X = <x1,x2,...,xn>和Y = <y1,y2,...ym>为两个序列,Z = <z1,z2,...,zk>为X和Y的任意LCS。

  • 如果xn = ym,则zk = xn = ym且Zk - 1是Xn - 1和Ym - 1的一个LCS;
  • 如果xn ≠ ym且zk ≠ xn,说明Z是Xn - 1和Y的一个LCS;
  • 如果xn ≠ ym且zk ≠ ym,说明Z是X和Ym的一个LCS;

  然后,我们开始建立最优解的递归式。定义dp[i][j]为Xi和Yj的LCS长度。显然的,当i = 0或j = 0时,LCS = 0。再结合最优子结构定理,可得到递归式:

                (    0;                              当 i = 0 或 j = 0;
dp[i][j] = { dp[i - 1][j - 1] + 1 当 i,j > 0 且 xi = yj;
( max(dp[i][j - 1], dp[i - 1][j]) 当 i,j > 0 且 xi != yj.

  得到递归式后,就可以根据公式写出递归算法或动态规划算法:

#include <iostream>
#include <string>
#include <vector>
#include <minmax.h> class Solution {
public:
int LongestCommonSubsequence(std::string s1, std::string s2) {
if (s1.empty() || s2.empty())
return 0;
std::vector<std::vector<int> > dp(s1.size(), std::vector<int>(s2.size(), 0));
int length = 0;
for (int i = 1; i < dp.size(); i++) {
for (int j = 1; j < dp[0].size(); j++) {
if (s1[i] == s2[j])
dp[i][j] = dp[i - 1][j - 1] + 1;
else
dp[i][j] = max(dp[i][j - 1], dp[i - 1][j]);
length = max(length, dp[i][j]);
}
}
return length + 1;
}
}; int main()
{
Solution solve;
std::string s1{"ABCBDAB"};
std::string s2{"ABDCABA"}; std::cout << solve.LongestCommonSubsequence(s1, s2) << std::endl; return 0;
}

  最后,类似的问题还有LIS(最长递增子序列)、LPS(最长回文子串)等等。

  参考资料:

    《算法导论》

LCS(最长公共子序列)的更多相关文章

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

    出处 http://segmentfault.com/blog/exploring/ 本章讲解:1. LCS(最长公共子序列)O(n^2)的时间复杂度,O(n^2)的空间复杂度:2. 与之类似但不同的 ...

  2. POJ 1458 Common Subsequence(LCS最长公共子序列)

    POJ 1458 Common Subsequence(LCS最长公共子序列)解题报告 题目链接:http://acm.hust.edu.cn/vjudge/contest/view.action?c ...

  3. 动态规划模板2|LCS最长公共子序列

    LCS最长公共子序列 模板代码: #include <iostream> #include <string.h> #include <string> using n ...

  4. LCS 最长公共子序列

    区别最长公共子串(连续) ''' LCS 最长公共子序列 ''' def LCS_len(x, y): m = len(x) n = len(y) dp = [[0] * (n + 1) for i ...

  5. LCS最长公共子序列(最优线性时间O(n))

    这篇日志主要为了记录这几天的学习成果. 最长公共子序列根据要不要求子序列连续分两种情况. 只考虑两个串的情况,假设两个串长度均为n. 一,子序列不要求连续. (1)动态规划(O(n*n)) (转自:h ...

  6. LCS最长公共子序列

    问题:最长公共子序列不要求所求得的字符串在所给字符串中是连续的,如输入两个字符串ABCBDAB和BDCABA,字符串BCBA和BDAB都是他们的公共最长子序列 该问题属于动态规划问题 解答:设序列X= ...

  7. LCS最长公共子序列HDU1159

    最近一直在学习算法,基本上都是在学习动态规划以及字符串.当然,两者交集最经典之一则是LCS问题. 首先LCS的问题基本上就是在字符串a,b之间找到最长的公共子序列,比如 YAOLONGBLOG 和 Y ...

  8. POJ 2250(LCS最长公共子序列)

    compromise Time Limit:1000MS     Memory Limit:65536KB     64bit IO Format:%I64d & %I64u   Descri ...

  9. LCS最长公共子序列~dp学习~4

    题目连接:http://acm.hdu.edu.cn/showproblem.php?pid=1513 Palindrome Time Limit: 4000/2000 MS (Java/Others ...

  10. Atcoder F - LCS (DP-最长公共子序列,输出字符串)

    F - LCS Time Limit: 2 sec / Memory Limit: 1024 MB Score : 100100 points Problem Statement You are gi ...

随机推荐

  1. composer install报错intervention/image 2.4.x-dev requires ext-fileinfo * -> the requested PHP extension fileinfo is missing from your system.

    (1)问题:intervention/image 2.4.x-dev requires ext-fileinfo * -> the requested PHP extension fileinf ...

  2. web前端技能考核(阿里巴巴)

  3. ENS中文文档系列之三 [ ENS常见问题 ]

    原文地址:https://ensuser.com/docs/frequently-asked-questions.html更多最新信息,请前往 ENS 中文服务站点:ENSUser 关于 ENS 注册 ...

  4. 201771010135 杨蓉庆/张燕《面对对象程序设计(java)》第十三周学习总结

    1.实验目的与要求 (1) 掌握事件处理的基本原理,理解其用途: (2) 掌握AWT事件模型的工作机制: (3) 掌握事件处理的基本编程模型: (4) 了解GUI界面组件观感设置方法: (5) 掌握W ...

  5. [转]工作量证明(PoW)权益证明(PoS)和委任权益证明(DPoS)区别

    原文链接 Both in the glossary and in some of our previous posts we've touched on mining and the two main ...

  6. Go语言基础之Path包与FilePath包

    文章引用自 path包的使用 package main; import ( "fmt" "path" ) //go语言path包的学习 func main() ...

  7. 【C语言】创建一个函数,判断某一正整数是否为素数,并调用这个函数找出1000以内所有素数

    #include <stdio.h> int fun(int x) { int n; ;n<=x-;n++) ) break; if(n>=x) ; else ; } main ...

  8. Jekyll+Github个人博客构建之路

    请参考: http://robotkang.cc/2017/03/HowToCreateBlog/

  9. pip 换源

    pip 换源 pip国内的一些镜像   阿里云 http://mirrors.aliyun.com/pypi/simple/   中国科技大学 https://pypi.mirrors.ustc.ed ...

  10. Tarjan's algorithm

    Tarjan算法可以用来求有向图的强连通分量个数,之前十分粗略的写了Kosaraju算法,这里打算比较认真的分析一下Tarjan算法,然后给出算法实现代码. Tarjan算法的主要算法部分也是dfs( ...