首先来看什么是最长公共子序列:给定两个序列,找到两个序列中均存在的最长公共子序列的长度。子序列需要以相关的顺序呈现,但不必连续。例如,“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)的更多相关文章

  1. [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 ...

  2. Dynamic Programming | Set 3 (Longest Increasing Subsequence)

    在 Dynamic Programming | Set 1 (Overlapping Subproblems Property) 和 Dynamic Programming | Set 2 (Opti ...

  3. 动态规划求最长公共子序列(Longest Common Subsequence, LCS)

    1. 问题描述 子串应该比较好理解,至于什么是子序列,这里给出一个例子:有两个母串 cnblogs belong 比如序列bo, bg, lg在母串cnblogs与belong中都出现过并且出现顺序与 ...

  4. [Algorithms] Longest Common Subsequence

    The Longest Common Subsequence (LCS) problem is as follows: Given two sequences s and t, find the le ...

  5. 1143. Longest Common Subsequence

    link to problem Description: Given two strings text1 and text2, return the length of their longest c ...

  6. 最长公共字串算法, 文本比较算法, longest common subsequence(LCS) algorithm

    ''' merge two configure files, basic file is aFile insert the added content of bFile compare to aFil ...

  7. LintCode Longest Common Subsequence

    原题链接在这里:http://www.lintcode.com/en/problem/longest-common-subsequence/ 题目: Given two strings, find t ...

  8. [UCSD白板题] Longest Common Subsequence of Three Sequences

    Problem Introduction In this problem, your goal is to compute the length of a longest common subsequ ...

  9. LCS(Longest Common Subsequence 最长公共子序列)

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

随机推荐

  1. 《算法导论》——随机化快排RandomizedQuickSort

    今日算法:随机化快排RandomizedQuickSort 基础工作swap交换和partition分治 /* *交换数组的两个元素 *fromIndex和toIndex为要交换的两个元素的索引 */ ...

  2. .NET项目中使用PostSharp

    PostSharp是一种Aspect Oriented Programming 面向切面(或面向方面)的组件框架,适用在.NET开发中,本篇主要介绍Postsharp在.NET开发中的相关知识,以及一 ...

  3. Android下的几种时间格式转换

    更多更全的工具类,请参考github上的Blankj/AndroidUtilCode 将毫秒转换为小时:分钟:秒格式 public static String ms2HMS(int _ms){ Str ...

  4. leetcode11

    public class Solution { //public int MaxArea(int[] height) //{ // var max = 0; // for (int i = 0; i ...

  5. python中类与对象及其绑定方法的定义

    面向对象编程 什么是面向对象? 面向过程:将需要解决的问题按步骤划分,一步一步完成每一个步骤,而且          步骤之间有联系. 优点:复杂问题可以分步完成 缺点:扩展性很差,维护性差.如果中间 ...

  6. 一直觉得用java很不顺心

    一直觉得用java很不顺心,今儿想明白一个事情.如果把汇编比作石器时代,c作为冷兵器时代,c++作为工业革命之后的热兵器及机械化时代,而c#之类则进入了现代科学世界,至于go,python之流,大概可 ...

  7. MySQL 物理文件体系结构的简单整理说明

    本文出处:http://www.cnblogs.com/wy123/p/7102128.html (保留出处并非什么原创作品权利,本人拙作还远远达不到,仅仅是为了链接到原文,因为后续对可能存在的一些错 ...

  8. Spring再接触 模拟Spring

    项目分层: 1.最土的方法是直接写到main中去 2.分出model层 2.如下 4.在抽象一个对数据库的访问层(跨数据库实现) 面向抽象编程 User.java package com.bjsxt. ...

  9. html 中使用 iconfont、fontAwesome

    在HTML中尽量使用iconfont 替代图片有很多好处,而且方便,可以设置大小.颜色 等 可以用于字体的设置. 一.使用iconfont 1.打开iconfont 官网 iconfont.cn. 2 ...

  10. UGUI中Text的换行

    通过代码中的\n可以直接执行换行效果,但是我们在平常的工作中一般都是读表,既在Inspector面板中的Text组件中输入同样的内容就达不到换行效果: 其实unity把\n转变成了\\n,我们只需要变 ...