一.最长公共子序列问题(LCS问题)

给定两个字符串A和B,长度分别为m和n,要求找出它们最长的公共子序列,并返回其长度。例如:

  A = "HelloWorld"

    B = "loop"

则A与B的最长公共子序列为 "loo",返回的长度为3。此处只给出动态规划的解法:定义子问题dp[i][j]为字符串A的第一个字符到第 i 个字符串和字符串B的第一个字符到第 j 个字符的最长公共子序列,如A“app”,B“apple”dp[2][3]表示 “ap” 和 “app” 的最长公共字串。注意到代码中 dp 的大小为 (n + 1) x (m + 1) ,这多出来的一行和一列是第 行和第 列,初始化为 0,表示空字符串和另一字符串的子串的最长公共子序列,例如dp[0][3]表示  "" 和 “app” 的最长公共子串。

当我们要求dp[i][j],我们要先判断A的第i个元素B的第j个元素是否相同即判断A[i - 1]B[j -1]是否相同,如果相同它就是dp[i-1][j-1]+ 1,相当于在两个字符串都去掉一个字符时的最长公共子序列再加 1;否则最长公共子序列dp[i][j - 1] dp[i - 1][j]中大者。所以整个问题的初始状态为:
 $$ dp[i][0] =0 , dp[0][j] = 0$$
相应的状态转移方程为:
$$  dp[i][j] = \begin{cases} \max\{dp[i - 1][j],dp[i][j - 1]\} ,& {A[i - 1]  != B[j - 1]} \\ dp[i - 1][j - 1] + 1 , & {A[i - 1]  == B[j - 1]} \end{cases}  $$
代码的实现如下:
class LCS
{
public:
int findLCS(string A, int n, string B, int m)
{
if(n == 0 || m == 0)//特殊输入
return 0;
int dp[n + 1][m + 1];//定义状态数组
for(int i = 0 ; i <= n; i++)//初始状态
dp[i][0] = 0;
for(int i = 0; i <= m; i++)
dp[0][i] = 0;
for(int i = 1; i <= n; i++)
for(int j = 1; j<= m; j++)
{
if(A[i - 1] == B[j - 1])//判断A的第i个字符和B的第j个字符是否相同
dp[i][j] = dp[i -1][j - 1] + 1;
else
dp[i][j] = max(dp[i - 1][j],dp[i][j - 1]);
}
return dp[n][m];//最终的返回结果就是dp[n][m]
}
};

该算法的时间复杂度为O(n*m),空间复杂度为O(n*m)。此外,由于遍历时是从下标1开始的,因为下标为0表示空字符串;所以第A的第i个字符实际上为A[i -1],B的第j个字符为B[j-1]。

二.最长公共子串问题

给定两个字符串A和B,长度分别为m和n,要求找出它们最长的公共子串,并返回其长度。例如:

  A = "HelloWorld"

    B = "loop"

则A与B的最长公共子串为 "lo",返回的长度为2。我们可以看到子序列和子串的区别:子序列和子串都是字符集合的子集,但是子序列不一定连续,但是子串一定是连续的。同样地,这里只给出动态规划的解法:定义dp[i][j]表示以A中第i个字符结尾的子串和B中第j个字符结尾的子串的的最大公共子串(公共子串实际上指的是这两个子串的所有部分)的长度(要注意这里和LCS的不同,LCS中的dp[i+1][j+1]一定是大于等于dp[i][j]的;但最长公共子串问题就不一定了,它的dp[i][j]表示的子串不一定是以A[0]开头B[0]开头的,但是一定是以A[i-1]、B[j-1]结尾的),同样地, dp 的大小也为 (n + 1) x (m + 1) ,这多出来的一行和一列是第 行和第 列,初始化为 0,表示空字符串和另一字符串的子串的最长公共子串。

当我们要求dp[i][j],我们要先判断A的第i个元素B的第j个元素是否相同即判断A[i - 1]和 B[j -1]是否相同,如果相同它就是dp[i - 1][j- 1] + 1,相当于在两个字符串都去掉一个字符时的最长公共子串再加 1;否则最长公共子串取0。所以整个问题的初始状态为:

$$ dp[i][0] =0 , dp[0][j] = 0$$

相应的状态转移方程为:
$$  dp[i][j] = \begin{cases} 0 ,& {A[i - 1]  != B[j - 1]} \\ dp[i - 1][j - 1] + 1 , & {A[i - 1]  == B[j - 1]} \end{cases}  $$
代码的实现如下:
class LongestSubstring {
public:
int findLongest(string A, int n, string B, int m) {
if(n == 0 || m == 0)
return 0;
int rs = 0;
int dp[n + 1][m + 1];
for(int i = 0 ; i <= n; i++)//初始状态
dp[i][0] = 0;
for(int i = 0; i <= m; i++)
dp[0][i] = 0;
for(int i = 1; i <= n; i++)
for(int j = 1; j<= m; j++)
{
if(A[i - 1] == B[j - 1])
{
dp[i][j] = dp[i -1][j - 1] + 1;
rs = max(rs,dp[i][j]);//每次更新记录最大值
} else//不相等的情况
dp[i][j] = 0;
}
return rs;//返回的结果为rs
}
};

该算法的时间复杂度为O(n*m),空间复杂度为O(n*m)。同样地,遍历下标也是从1开始的。不过关于最长公共子串问题,有几点需要注意下:

1.由于dp[i][j]不像LCS是个递增的数组,所以它在每次更新时需要同时更新最大值rs,且最后返回的结果是rs。而LCS中返回的直接就是dp[n][m]。

2.从代码上来看,两者的结构其实差不多,只不过状态转移方程有些小许的不同,分析过程也类似。

3.另外,关于这量两种问题还有更优的解法,不过本文主要是DP的思想去解决,当然其中还有对DP的优化,不过此处不再详述。

参考:https://www.nowcoder.com/questionTerminal/c996bbb77dd447d681ec6907ccfb488a

   https://blog.csdn.net/u012102306/article/details/53184446

动态规划经典——最长公共子序列问题 (LCS)和最长公共子串问题的更多相关文章

  1. 最长递增子序列(lis)最长公共子序列(lcs) 最长公共上升子序列(lics)

    lis: 复杂度nlgn #include<iostream> #include<cstdio> using namespace std; ],lis[],res=; int ...

  2. 最长公共子序列(LCS)最长递增子序列(LIS)

    #include<cstring>#include<iostream>#include<stack>#include <algorithm>using ...

  3. 删除部分字符使其变成回文串问题——最长公共子序列(LCS)问题

    先要搞明白:最长公共子串和最长公共子序列的区别.    最长公共子串(Longest Common Substirng):连续 最长公共子序列(Longest Common Subsequence,L ...

  4. 最长公共子序列(LCS问题)

    先简单介绍下什么是最长公共子序列问题,其实问题很直白,假设两个序列X,Y,X的值是ACBDDCB,Y的值是BBDC,那么XY的最长公共子序列就是BDC.这里解决的问题就是需要一种算法可以快速的计算出这 ...

  5. 【Luogu P1439】最长公共子序列(LCS)

    Luogu P1439 令f[i][j]表示a的前i个元素与b的前j个元素的最长公共子序列 可以得到状态转移方程: if (a[i]==b[j]) dp[i][j]=dp[i-1][j-1]+1; d ...

  6. 最长公共子序列(LCS)、最长递增子序列(LIS)、最长递增公共子序列(LICS)

    最长公共子序列(LCS) [问题] 求两字符序列的最长公共字符子序列 问题描述:字符序列的子序列是指从给定字符序列中随意地(不一定连续)去掉若干个字符(可能一个也不去掉)后所形成的字符序列.令给定的字 ...

  7. nyoj 36 最长公共子序列【LCS模板】

    最长公共子序列 时间限制:3000 ms  |  内存限制:65535 KB 难度:3   描述 咱们就不拐弯抹角了,如题,需要你做的就是写一个程序,得出最长公共子序列.tip:最长公共子序列也称作最 ...

  8. 最长公共子序列问题 (LCS)

    给定两个字符串S和T.求出这两个字符串最长的公共子序列的长度. 输入: n=4 m=4 s="abcd" t="becd" 输出: 3("bcd&qu ...

  9. 动态规划法(十)最长公共子序列(LCS)问题

    问题介绍   给定一个序列\(X=<x_1,x_2,....,x_m>\),另一个序列\(Z=<z_1,z_2,....,z_k>\)满足如下条件时称为X的子序列:存在一个严格 ...

随机推荐

  1. Cassandra集群:一,搭建一个三节点的集群

    环境准备 JDK1.8 http://download.oracle.com/otn/java/jdk/8u171-b11/512cd62ec5174c3487ac17c61aaa89e8/jdk-8 ...

  2. Python学习——多线程,异步IO,生成器,协程

    Python的语法是简洁的,也是难理解的. 比如yield关键字: def fun(): for i in range(5): print('test') x = yield i print('goo ...

  3. Python之时间(time)模块

    在Python中,通常有这几种方式来表示时间: 时间戳(timestamp):通常来说,时间戳表示的是从1970年1月1日00:00:00开始按秒计算的偏移量.我们运行“type(time.time( ...

  4. Swiper.js使用及API介绍

    Swiper.js详细使用教程http://www.swiper.com.cn/api/start/2014/1218/140.html

  5. GDT全局描述符表

    GDT全局描述符表 什么是GDT全局描述符表 GDT全称为Global Descriptor Table,全局描述符表. 保护模式的寻址方式不在使用寄存器分段的方式直接寻址方式了.而采用的是使用GDT ...

  6. GeoServer java.io.IOException: No such resource: generic.sld No such resource: generic.sld

    原因是 发布 图层时 没有设置类型 默认 generic 但是我们的数据库中 没有这个 解决办法: 点击 图层--点击 相应的 图层名称 ---发布  --- WMS Settings 下面的Defa ...

  7. opcode cache与JIT的区别

    要说明opcode cache与JIT的区别,得先明白,字节码,又叫中间码与机器码的区别. 操作码(opcode) 一条机器指令.比如我们汇编语言写的一条操作语句. 机器码(machine code) ...

  8. 如何利用一台pc获取百万利益 《标题党》

    这是我在quora上看到的一个问题,我看到的被推荐的答案的第一句话就很nb. 有想法很容易做起来很难(不是这句) I’m going to give you something much more v ...

  9. 剑指Offer 8. 跳台阶 (递归)

    题目描述 一只青蛙一次可以跳上1级台阶,也可以跳上2级.求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果). 题目地址 https://www.nowcoder.com/pract ...

  10. mysql中外键的创建与删除

    外键的创建 方法1:创建表的时候设置(外键名随机生成) 1.前提条件,必须要有一个主表,这里设为persons 2.主表中必须设置主键字段primary key,这里设为id_p //创建数据库dat ...