Dynamic Programming | Set 4 (Longest Common Subsequence)
首先来看什么是最长公共子序列:给定两个序列,找到两个序列中均存在的最长公共子序列的长度。子序列需要以相关的顺序呈现,但不必连续。例如,“abc”, “abg”, “bdf”, “aeg”, ‘”acefg”等都是“abcdefg”的子序列。因此,一个长度为n的序列拥有2^n中可能的子序列(序列中的每一个元素只有选或者不选两种可能,因此是2^n)。
Example:
LCS for input Sequences “ABCDGH” and “AEDFHR” is “ADH” of length 3.
LCS for input Sequences “AGGTAB” and “GXTXAYB” is “GTAB” of length 4.
该问题最普通的解法是对两个给定序列分别生成所有子序列,然后找到最长的匹配的子序列。这样的解法是指数复杂度的,显然不是我们需要的。我们来看该问题是如何拥有动态规划问题的重要性质的。
1 Optimal Substructure:
假设输入序列分别为长度为m的X[0..m-1]和长度为n的Y[0..n-1],令L(X[0..m-1], Y[0..n-1])为序列X、Y的最长公共子序列的长度,如下为L(X[0..m-1], Y[0..n-1])的递归定义:
If last characters of both sequences match (or X[m-1] == Y[n-1]) then
L(X[0..m-1], Y[0..n-1]) = 1 + L(X[0..m-2], Y[0..n-2])
If last characters of both sequences do not match (or X[m-1] != Y[n-1]) then
L(X[0..m-1], Y[0..n-1]) = MAX ( L(X[0..m-2], Y[0..n-1]), L(X[0..m-1], Y[0..n-2])
例子:
1) Consider the input strings “AGGTAB” and “GXTXAYB”. Last characters match for the strings. So length of LCS can be written as:
L(“AGGTAB”, “GXTXAYB”) = 1 + L(“AGGTA”, “GXTXAY”)
2) Consider the input strings “ABCDGH” and “AEDFHR. Last characters do not match for the strings. So length of LCS can be written as:
L(“ABCDGH”, “AEDFHR”) = MAX ( L(“ABCDG”, “AEDFHR”), L(“ABCDGH”, “AEDFH”) )
因此,LCS问题具有最优子结构性质,可以使用求解子问题的方案来解决。
2 Overlapping Subproblems:
如下是LCS问题的递归求解程序,该实现遵循了上面的递归结构:
/* A Naive recursive implementation of LCS problem */
#include<stdio.h>
#include<stdlib.h> int max(int a, int b); /* Returns length of LCS for X[0..m-1], Y[0..n-1] */
int lcs( char *X, char *Y, int m, int n )
{
if (m == 0 || n == 0)
return 0;
if (X[m-1] == Y[n-1])
return 1 + lcs(X, Y, m-1, n-1);
else
return max(lcs(X, Y, m, n-1), lcs(X, Y, m-1, n));
} /* Utility function to get max of 2 integers */
int max(int a, int b)
{
return (a > b)? a : b;
} /* Driver program to test above function */
int main()
{
char X[] = "AGGTAB";
char Y[] = "GXTXAYB"; int m = strlen(X);
int n = strlen(Y); printf("Length of LCS is %d\n", lcs( X, Y, m, n ) ); getchar();
return 0;
}
以上程序的时间复杂度在最坏情况下是O(2^n),最坏情况是X与Y中的所有字符均不匹配,也就是说LCS的长度为0。
根据上面的实现,如下是当输入序列为“AXYT”和“AYZX”时的部分递归树:
不难发现,lcs(“AXY”, “AYZ”) 被计算了2次。如果我们画出完整的递归树,会找到更多被重复计算的子问题。因此,该问题具备重叠子结构性质,可以通过Memoization或者Tabulation来避免重复计算。下面是LCS问题的Tabulation实现。
/* Dynamic Programming implementation of LCS problem */
#include<stdio.h>
#include<stdlib.h> int max(int a, int b); /* Returns length of LCS for X[0..m-1], Y[0..n-1] */
int lcs( char *X, char *Y, int m, int n )
{
int L[m+1][n+1];
int i, j; /* Following steps build L[m+1][n+1] in bottom up fashion. Note
that L[i][j] contains length of LCS of X[0..i-1] and Y[0..j-1] */
for (i=0; i<=m; i++)
{
for (j=0; j<=n; j++)
{
if (i == 0 || j == 0)
L[i][j] = 0; else if (X[i-1] == Y[j-1])
L[i][j] = L[i-1][j-1] + 1; else
L[i][j] = max(L[i-1][j], L[i][j-1]);
}
} /* L[m][n] contains length of LCS for X[0..n-1] and Y[0..m-1] */
return L[m][n];
} /* Utility function to get max of 2 integers */
int max(int a, int b)
{
return (a > b)? a : b;
} /* Driver program to test above function */
int main()
{
char X[] = "AGGTAB";
char Y[] = "GXTXAYB"; int m = strlen(X);
int n = strlen(Y); printf("Length of LCS is %d\n", lcs( X, Y, m, n ) ); getchar();
return 0;
}
以上实现的时间复杂度为O(mn),相比原始递归求解的最坏情况要好太多了。
上面的程序只是返回了LCS的长度,可以参照该文章来打印LCS Printing Longest Common Subsequence 。
Dynamic Programming | Set 4 (Longest Common Subsequence)的更多相关文章
- [Algorithms] Using Dynamic Programming to Solve longest common subsequence problem
Let's say we have two strings: str1 = 'ACDEB' str2 = 'AEBC' We need to find the longest common subse ...
- Dynamic Programming | Set 3 (Longest Increasing Subsequence)
在 Dynamic Programming | Set 1 (Overlapping Subproblems Property) 和 Dynamic Programming | Set 2 (Opti ...
- 动态规划求最长公共子序列(Longest Common Subsequence, LCS)
1. 问题描述 子串应该比较好理解,至于什么是子序列,这里给出一个例子:有两个母串 cnblogs belong 比如序列bo, bg, lg在母串cnblogs与belong中都出现过并且出现顺序与 ...
- [Algorithms] Longest Common Subsequence
The Longest Common Subsequence (LCS) problem is as follows: Given two sequences s and t, find the le ...
- 1143. Longest Common Subsequence
link to problem Description: Given two strings text1 and text2, return the length of their longest c ...
- 最长公共字串算法, 文本比较算法, longest common subsequence(LCS) algorithm
''' merge two configure files, basic file is aFile insert the added content of bFile compare to aFil ...
- LintCode Longest Common Subsequence
原题链接在这里:http://www.lintcode.com/en/problem/longest-common-subsequence/ 题目: Given two strings, find t ...
- [UCSD白板题] Longest Common Subsequence of Three Sequences
Problem Introduction In this problem, your goal is to compute the length of a longest common subsequ ...
- LCS(Longest Common Subsequence 最长公共子序列)
最长公共子序列 英文缩写为LCS(Longest Common Subsequence).其定义是,一个序列 S ,如果分别是两个或多个已知序列的子序列,且是所有符合此条件序列中最长的,则 S 称为已 ...
随机推荐
- 国内+海外IDC资源合作
主营业务:服务器租用.托管.机柜大带宽.安全防御.云主机.海外专线.海外托管.CDN加速.站群 资源覆盖: 华南:广东东莞.深圳.广州.湛江.福建厦门.泉州.福州 华北:北京.天津.山东 华东:江苏苏 ...
- spring boot 错误处理之深度历险
今天终于把 boot 的异常处理完全研究透了: boot提供了很多错误的处理工作.默认情况下,我们会看到一个whiteLabel(白标)的页面. 这个可能不是我们所需.因此我们需要定制.我于是做了个深 ...
- html常见的块元素和行内元素(特别注意个别块元素不能嵌套其他块元素)
html中常见的块元素:div.p.h1-h6.ul.ol.li.hr.table.pre等 块级元素新开启一行即使是设置了width属性也是独占一行(可设置float浮动属性调整布局).尽可能撑满父 ...
- PHP 调试打印输出变量
var_dump ($row); echo "hello"; echo "\n"; print_r ($arr); php 数组 对象 $arr = json_ ...
- poi横纵导出
dao <select id="selectTargetModel" resultMap="targetMap"> select si.SHOP_N ...
- HashMap理解
HashMap中Capacity为数组长度,默认大小为16,size为元素个数,loadFactor为size/capacity,默认为0.75,当存储的元素个数size大于等于capacity乘以0 ...
- jquery之find,filter,has对比
find()方法找的是符合条件的后代,返回的是子元素. $('div').find('.intro').css('color','red'); //寻找div后代类为intro的元素 filter() ...
- linux下安装haproxy作为端口转发服务器,以及安装keepalived作为haproxy高可用方案
一.安装haproxy作为端口转发服务器(主服务器:172.28.5.4,备服务器:172.28.5.8,浮点IP为:172.28.5.6) 1.安装依赖包 yum -y install wget g ...
- 大端&小端问题
- 正确理解c和c ++的复杂类型声明
本文作者girlrong是网易广州社区的C语言版版主,这篇文章被选在精华区.很是不错,不敢独享!据说她乐于助人,虚心诚恳,颇受网友欢迎.只可惜现在已退隐江湖了.在最近学习C语言过程中,了解些前辈大牛的 ...