最长公共子序列也是动态规划中的一个经典问题。

有两个字符串 S1 和 S2,求一个最长公共子串,即求字符串 S3,它同时为 S1 和 S2 的子串,且要求它的长度最长,并确定这个长度。这个问题被我们称为最长公共子序列问题。

与求最长递增子序列一样,我们首先将原问题分割成一些子问题,我们用 dp[i][j]表示 S1 中前 i 个字符与 S2 中前 j 个字符分别组成的两个前缀字符串的最长公共子串长度。

显然的,当 i、 j 较小时我们可以直接得出答案,如 dp[0][j]必等于 0。那么,假设我们已经求得 dp[i][j](0<=i<x,0<=j<y)的所有值,考虑如何由这些值继而推得 dp[x][y],求得 S1 前 x 个字符组成的前缀子串和 S2 前 y 个字符 组成的前缀子串的最长公共子序列长度。若 S1[x] = =S2[y],即 S1 中的第 x 个字 符和 S2 中的第 y 个字符相同,同时由于他们都是各自前缀子串的最后一个字符, 那么必存在一个最长公共子串以 S1[x]或 S2[y]结尾,其它部分等价于 S1 中前 x-1 个字符和 S2中前 y-1个字符的最长公共子串。所以这个子串的长度比 dp[x-1][y-1] 又增加 1,即 dp[x][y] = dp[x-1][y-1] + 1。

相反的,若 S1[x] != S2[y],此时其最长 公共子串长度为 S1 中前 x-1 个字符和 S2 中前 y 个字符的最长公共子串长度与 S1 中前 x 个字符和 S2 中前 y-1 个字符的最长公共子串长度的较大者,即在两种 情况下得到的最长公共子串都不会因为其中一个字符串又增加了一个字符长度 发生改变。综上所述, dp[x][y] = max{dp[x-1][y],dp[x][y-1]}。

最长公共子序列的递推条件

假设有两个字符串S1和S2,其中S1的长度为n,S2的长度为m,用dp[i][j]表示S1前i个字符组成的前缀子串与S2前j个字符组成的前缀子串的最长公共子串长度,如下:

dp[0][j] = dp[i][0] = 0,其中j>=0 && j<=m,i>=0 && i<=n;

dp[i][j] = dp[i-1][j-1]+1;(S1[i] = S2[j])

dp[i][j] = max{dp[i-1][j],dp[i][j-1]};(S1[i] != S2[j])

最后可以求得dp[n][m]中保存的值即为两个原始字符串的最长公共子序列长度。

按照上面的公式可以写出最长公共子序列的算法

#include "stdafx.h"
#include <iostream>
#include <string>
using namespace std; #define MAXSIZE 101
char str1[MAXSIZE];
char str2[MAXSIZE];
//'l'表示dp[i][j] = dp[i][j] = dp[i - 1][j];
//‘q’表示dp[i][j] = dp[i][j] = dp[i - 1][j];
//'u'表示dp[i][j] = dp[i][j - 1];
char path[MAXSIZE][MAXSIZE];
int dp[MAXSIZE][MAXSIZE]; void printLCS(int i, int j)
{
if (i == || j == )
return;
if (path[i][j] == 'q')
{
printLCS(i - , j - );
cout << str1[i-] << ' ';
}
else if (path[i][j] == 'u')
printLCS(i - , j);
else
printLCS(i, j - ); } int main()
{
int n, m;
cin >> str1 >> str2;
n = strlen(str1);
m = strlen(str2);
//初始化
for (int i = ; i < n;i++)
for (int j = ; j < m; j++)
dp[i][j] = ;
for (int i = ; i <= n; i++)
for (int j = ; j <= m; j++)
{
if (str1[i - ] == str2[j - ])
{
dp[i][j] = dp[i - ][j - ] + ;
path[i][j] = 'q';
} else
{
if (dp[i - ][j] >= dp[i][j - ])
{
dp[i][j] = dp[i - ][j];
path[i][j] = 'u';
} else
{
dp[i][j] = dp[i][j - ];
path[i][j] = 'l';
} }
} cout << dp[n][m] << endl;
printLCS(n, m);
return ;
}

测试实例:

str1 = “abcbdab”;str2 = "bdcaba"。

输出如下:

虽然str1和str2的最长公共子序列有多个,根据dp[n][m]进行递归输出,只输出一个最长公共子序列。

动态规划 - 最长公共子序列(LCS)的更多相关文章

  1. 动态规划 最长公共子序列 LCS,最长单独递增子序列,最长公共子串

    LCS:给出两个序列S1和S2,求出的这两个序列的最大公共部分S3就是就是S1和S2的最长公共子序列了.公共部分 必须是以相同的顺序出现,但是不必要是连续的. 选出最长公共子序列.对于长度为n的序列, ...

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

    题目: 求解两个字符串的最长公共子序列.如 AB34C 和 A1BC2   则最长公共子序列为 ABC. 思路分析:可以用dfs深搜,这里使用到了前面没有见到过的双重循环递归.也可以使用动态规划,在建 ...

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

    摘自 https://www.cnblogs.com/hapjin/p/5572483.html 这位大佬写的对理解DP也很有帮助,我就直接摘抄过来了,代码部分来自我做过的题 一,问题描述 给定两个字 ...

  4. 动态规划之最长公共子序列LCS(Longest Common Subsequence)

    一.问题描述 由于最长公共子序列LCS是一个比较经典的问题,主要是采用动态规划(DP)算法去实现,理论方面的讲述也非常详尽,本文重点是程序的实现部分,所以理论方面的解释主要看这篇博客:http://b ...

  5. 《算法导论》读书笔记之动态规划—最长公共子序列 & 最长公共子串(LCS)

    From:http://my.oschina.net/leejun2005/blog/117167 1.先科普下最长公共子序列 & 最长公共子串的区别: 找两个字符串的最长公共子串,这个子串要 ...

  6. 编程算法 - 最长公共子序列(LCS) 代码(C)

    最长公共子序列(LCS) 代码(C) 本文地址: http://blog.csdn.net/caroline_wendy 题目: 给定两个字符串s,t, 求出这两个字符串最长的公共子序列的长度. 字符 ...

  7. C++版 - Lintcode 77-Longest Common Subsequence最长公共子序列(LCS) - 题解

    版权声明:本文为博主Bravo Yeung(知乎UserName同名)的原创文章,欲转载请先私信获博主允许,转载时请附上网址 http://blog.csdn.net/lzuacm. C++版 - L ...

  8. 1006 最长公共子序列Lcs

    1006 最长公共子序列Lcs 基准时间限制:1 秒 空间限制:131072 KB 给出两个字符串A B,求A与B的最长公共子序列(子序列不要求是连续的). 比如两个串为: abcicba abdks ...

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

    POJ1458 Common Subsequence(最长公共子序列LCS) http://poj.org/problem?id=1458 题意: 给你两个字符串, 要你求出两个字符串的最长公共子序列 ...

  10. 51Nod 1006:最长公共子序列Lcs(打印LCS)

    1006 最长公共子序列Lcs  基准时间限制:1 秒 空间限制:131072 KB 分值: 0 难度:基础题  收藏  关注 给出两个字符串A B,求A与B的最长公共子序列(子序列不要求是连续的). ...

随机推荐

  1. Wampserver #1045 无法登录 mysql 服务器

    一是通过phpMyAdmin直接修改: 二是使用WAMP的MySql控制台修改.   第一种: ①在phpMyAdmin界面中点击[用户],将用户概况中的所有用户名为[root]   用户的密码都改为 ...

  2. 2015百度之星1002 查找有序序列(RMQ+主席树模板水过)

    题意:求在数列中能找到几个个长度为k 的区间,里面的 k 个数字排完序后是连续的. 思路:枚举范围,判断区间内是否有重复的数字(主席树),没有的话求区间最大-区间最小(RMQ),判断是否等于K,是的话 ...

  3. [转]Redis实现分析

    Redis实现分析 浏览次数:1018次 KITERUNNER_T 2014年10月19日 字号: 大 中 小 分享到: QQ空间 新浪微博 腾讯微博 人人网 豆瓣网 开心网 更多 1   1 环境准 ...

  4. 避免每次输入bundler Exec命令

    bundle在ruby的世界里是个好东西,它可以用来管理应用程序的依赖库.它能自动的下载和安装指定的gem,也可以随时更新指定的gem. rvm则是一个命令行工具,能帮助你轻松的安装,管理多个ruby ...

  5. notifyDataSetInvalidated()跟notifyDataSetChanged()的区别

    public void notifyDataSetChanged(): 通过一个外部的方法控制,如果适配器的内容改变了,那么就会强制调用getView来刷新每个Item的内容.这个方法内部实现了在每个 ...

  6. linux dns 连外网

    以下设置对所用的Linux系统如Redhat/Ubuntu/Debian/CentOS等都有效,但您必须是管理员root或者具有管理员权限 vim /etc/resolv.conf 在其中加入: na ...

  7. for循环相关

    循环语句是指令式编程的常见语句,Scala对其加以改进,成为适应函数式风格的利器. for循环中的变量,没有val或者var,是因为变量的类型,完全是集合中的元素的类型.作用域持续到括号结束. 在sc ...

  8. ipython

    ipython 是一个 python 的交互式 shell,比默认的python shell 好用得多,支持变量自动补全,自动缩进,支持 bash shell 命令,内置了许多很有用的功能和函数. T ...

  9. 源码分析:Java对象的内存分配

    Java对象的分配,根据其过程,将其分为快速分配和慢速分配两种形式,其中快速分配使用无锁的指针碰撞技术在新生代的Eden区上进行分配,而慢速分配根据堆的实现方式.GC的实现方式.代的实现方式不同而具有 ...

  10. lua的栈

    lua的栈是从栈底到栈顶: lua_pushstring(L, "test1");lua_pushstring(L, "test2");lua_pushstri ...