(为什么都更了这么多篇笔记了,这时候才讲这么基础的内容呢?因为我本来以为LCS这种简单的DP不用讲的,结果CF不久前考了LCS的变式,然后我发现由于自己对LCS一点都不熟,居然写不出来 ,于是决定还是写一下笔记。今后不排除你们还会看到最小生成树、背包等基础算法的笔记……)

最长公共子序列(Longest Common Subsequence,LCS)是动态规划中的经典问题,顾名思义,即求两个序列最长的公共子序列(可以不连续)。在本文中,我们规定用

表示序列
的最后一个元素,用
表示
去掉最后一个元素后的子序列,
表示s1和s2的LCS的长度。现在,假如我们有

abdcbabbdcbabb两个字符串,记为

,我们要如何求它们的LCS呢?

既然已经说了是动态规划问题,我们就考虑如何从子状态转移过来。这有两种情形,第一种,如果两个序列最后一个元素相同,比如abecbabbdcbabb,最后一个元素相同,所以它们的LCS长度就是abecbabdcbab的LCS长度加上1。这是因为,

的任何公共子序列
去掉最后一个元素后,都是
的公共子序列,所以
的长度不会大于

如果最后一个元素不同,则LCS长度应为

,因为这时两个序列的最后一个元素不会同时出现在LCS中。

当然,我们还需要处理一下边界条件,即当

中至少有一个为空时,这时LCS的长度为0。于是综合一下,我们可以得到以下式子:

我们可以轻松地把它翻译成一个递归程序(我直接用Python写了):

def lcs(s1,s2):if s1 == "" or s2 == "":return 0elif s1[-1] == s2[-1]:return lcs(s1[:-1], s2[:-1])+1else:return max(lcs(s1,s2[:-1]), lcs(s1[:-1],s2))

然而,稍微了解一点递归的原理的人都知道,直接这样写,必然引起时间、空间复杂度的爆炸。于是我们可以用记忆化的方法,并且传参时传索引而不是序列本身。

以下是HDU1159(LCS模板题)的AC代码:

#include <algorithm>
#include <cstring>
#include <iostream>
using namespace std;
const int MAXN = 505;
char s1[MAXN], s2[MAXN];
int dp[MAXN][MAXN];
int lcs(int n1, int n2)
{if (dp[n1][n2] != -1)return dp[n1][n2];if (n1 == 0 || n2 == 0)dp[n1][n2] = 0;else if (s1[n1 - 1] == s2[n2 - 1])dp[n1][n2] = lcs(n1 - 1, n2 - 1) + 1;elsedp[n1][n2] = max(lcs(n1, n2 - 1), lcs(n1 - 1, n2));return dp[n1][n2];
}
int main()
{while (cin >> s1 >> s2){memset(dp, -1, sizeof(dp));cout << lcs(strlen(s1), strlen(s2)) << endl;}return 0;
}

当然,为了节约栈空间,我们也可以用递推的方法,代码也很简洁:

while (cin >> s1 >> s2)
{memset(dp, 0, sizeof(dp));int n1 = strlen(s1), n2 = strlen(s2);for (int i = 1; i <= n1; ++i)for (int j = 1; j <= n2; ++j)if (s1[i - 1] == s2[j - 1])dp[i][j] = dp[i - 1][j - 1] + 1;elsedp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);cout << dp[n1][n2] << endl;
}

这一算法的时间复杂度和空间复杂度均为

,如果用滚动数组(即只维护两个一维数组,只保留

dp[i]dp[i-1]中各项的值)可以把空间复杂度优化到

输出方案

例如,我们最终得到了这样一个二维数组:

我们知道,如果某个dp[i][j]比它左上方的值大1,且s1[i-1]==s2[j-1](注意我这里用的是0-index所以要减1),说明可以选这个元素来增大LCS。于是我们可以用这样的策略:从右下角开始,如果有dp[i][j]==dp[i-1][j-1]+1则往左上走一格,如果同时还有s1[i-1]==s2[j-1]则将其加入;否则的话,它的左边或上边(根据公式)至少有一个与它相等,于是往与它相等的方向走一格;到左上角为止。这其实相当于把动态规划过程倒过来做了一遍。

最长公共子串

子串和子序列的区别在于,子串必须是连续的。求最长公共子串的长度和求最长公共子序列的长度的方法几乎一样,我们用dp[i][j]代表以

的第

i个元素、

的第

j个元素结尾的最长公共子串的长度。那么当s1[i-1]==s2[j-1]时递推公式与最长公共子序列的情形一致,但是当s1[i-1]!=s2[j-1] 时会直接为0。这样写的话,因为我们不知道最长公共子串是以那两个元素结尾的,所以我们就需要枚举ij来求最大值:

while (cin >> s1 >> s2)
{memset(dp, 0, sizeof(dp));int n1 = strlen(s1), n2 = strlen(s2), ma = 0;for (int i = 1; i <= n1; ++i)for (int j = 1; j <= n2; ++j)if (s1[i - 1] == s2[j - 1])dp[i][j] = dp[i - 1][j - 1] + 1;elsedp[i][j] = 0;for (int i = 1; i <= n1; ++i)for (int j = 1; j <= n2; ++j)ma = max(ma, dp[i][j]);cout << ma << endl;
}

得到的dp二维数组如下:

这个要输出答案就更简单了,找到使dp[i][j]最大的ij,将对应的串从ij处开始往前数dp[i][j]个元素,得到的子串就是最长公共子串。

变式

(CF1446B Catching Cheaters)

You are given two strings

and
representing essays of two students who are suspected cheaters. For any two strings CC, DD we define their similarity score
as
, where
denotes the length of the Longest Common

Subsequence of strings

and
.

You believe that only some part of the essays could have been copied, therefore you're interested in their substrings.
Calculate the maximal similarity score over all pairs of substrings. More formally, output maximal

over all pairs
, where
is some substring of
, and
is some substring of
.

If

is a string,
denotes its length.

A string

is a

substring of a string

if
can be obtained from
by deletion of several (possibly, zero or all) characters from the beginning and several (possibly, zero or all) characters from the end.

A string

is a

subsequence of a string

if aa can be obtained from
by deletion of several (possibly, zero or all) characters.

Pay attention to the difference between the substring and subsequence, as they both appear in the problem statement.
You may wish to read the Wikipedia page about the Longest Common Subsequence problem.Input
The first line contains two positive integers

and
(
) — lengths of the two strings
and
.

The second line contains a string consisting of nn lowercase Latin letters — string

.

The third line contains a string consisting of mm lowercase Latin letters — string

.

Output
Output maximal

over all pairs
, where
is some substring of
, and
is some substring of
.

Examplesinput
4 5
abba
babab output
5input
8 10
bbbbabab
bbbabaaaaa output
12input
7 7
uiibwws
qhtkxcn output
0

这个题其实就是最长公共子序列和最长公共子串的混合体,我们用dp[i][j]表示代表以

的第

i个元素、

的第

j个元素结尾的子串的最大

值。于是我们考虑转移,当

s1[i-1]==s2[j-1]时, 可以从dp[i-1][j-1]转移过来,

值加2;当

s1[i-1]!=s2[j-1]时,则是从dp[i-1][j]dp[i][j-1]中的最大值转移过来,

值减1(但减到小于0时取0,因为可以不取)。
for (int i = 1; i <= n1; ++i)for (int j = 1; j <= n2; ++j)if (s1[i - 1] == s2[j - 1])dp[i][j] = dp[i - 1][j - 1] + 2;elsedp[i][j] = max(max(dp[i - 1][j] - 1, dp[i][j - 1] - 1), 0);
for (int i = 1; i <= n1; ++i)for (int j = 1; j <= n2; ++j)ma = max(ma, dp[i][j]);


Pecco:算法学习笔记(目录)​zhuanlan.zhihu.com

两个字符串的最长公共子序列长度_算法学习笔记(58): 最长公共子序列相关推荐

  1. java最长公共子序列算法_算法学习——java实现最长公共子序列

    实验目的: 输入两个相同类型的序列,用动态规划方法计算他们的最长公共子序列的长度以及序列. 思路: 1.先用一个二维数组存储最长公共子序列的长度,还要记录每个值的状态 2.根据记录值的状态,递归回溯求 ...

  2. 最长公共子序列算法 java,算法学习——java实现最长公共子序列,

    算法学习--java实现最长公共子序列学习--java实现最长公共子序列的算法, 实验目的: 输入两个同类型的序列,用动态规划的方法计算它们最长的公共子序列的长度和序列. (推荐教程: Java视频教 ...

  3. 数据结构与算法 学习笔记(5):字符串

    数据结构与算法 学习笔记(5)- 字符串 本次笔记记录了LeetCode中关于字符串的一些问题,并给出了相应的思路说明和代码.题目编号与LeetCode对应,方便查找. 题目1:LeetCode 13 ...

  4. C语言字符串中最后一个单词的长度的算法(附完整源码)

    C语言字符串中最后一个单词的长度的算法 C语言字符串中最后一个单词的长度的算法完整源码(定义,实现,main函数测试) C语言字符串中最后一个单词的长度的算法完整源码(定义,实现,main函数测试) ...

  5. 两个字符串的最长公共子序列长度_【面试】动态规划-之最长公共子序列、最长公共子串问题...

    先来说明下什么是最长公共子序列,什么是是最长公共子串,举一个实际例子,myblogs与belong,最长公共子序列为blog(myblogs, belong),最长公共子串为lo(myblogs, b ...

  6. 两个字符串的最长公共子序列长度_程序员编程算法,解决文本相似度问题的最长公共子序列算法!...

    在前面我讲解了如何通过最长公共子串来求解两个文本的相似度问题,但它有一定缺陷,举个例子,看下面的两个字符串 我爱吃小青菜和各种鲜水果. 我很爱吃青菜与各样水果. 上面两个字符串,如果通过计算子串来求相 ...

  7. 两个字符串的最长公共子序列长度_求2个字符串的最长公共子序列和最长公共子字符串...

    一. 最长公共子序列 定义: 一个数列S,如果分别是两个或多个已知数列的子序列,且是所有符合此条件序列中最长的,则 S 称为已知序列的最长公共子序列. 例如:输入两个字符串BDCABA和 ABCBDA ...

  8. 两个字符串的最长公共子序列长度_输出两个字符串的最长公共子串和最长公共子序列...

    输出两个字符串的最长公共子串和最长公共子序列.求解两个字符串的最长公共子串和最长公共子序列在方法上很接近,都是动态规划.只不过在递推方程上有一些不一样. 求两个字符串的最长公共子串 #include ...

  9. c 最大子序列和_算法总结:左神class8—跳台阶+最长递增公共子序列

    [跳台阶]有n级台阶,一个人每次上一级或者两级,问有多少种走完n级台阶的方法? public int s1(int n){ if (n< 1){ return 0; if(n == 1 || n ...

最新文章

  1. 【ICML2021】具有持续进化策略的展开计算图的无偏梯度估计
  2. LruCache 源码解析
  3. Ardino基础教程 18_驱动74HC595
  4. 使用Maven搭建一个Web项目
  5. Delphi IDE扩展工具,在IDE中增加Google翻译器
  6. firefox 和 ie 事件处理的细节,研究,再研究-----书写同时兼容ie和ff的事件处理代码...
  7. php7 断言,PHP7.2常用新特性和函数
  8. 2020 泰迪杯 C 题
  9. 魔兽地图服务器修改,魔兽地图编辑器的问题,怎么修改RPG
  10. matplotlib颜色表
  11. 医院的智能物流系统如何搭建?
  12. Load和Initialize的区别和使用
  13. List把特定元素排在第一位
  14. nginx日志磁盘空间使用率100%
  15. 常见的数据库有哪些?
  16. 公众号创建菜单报错40001及菜单出现在先前公众号上的问题
  17. 计算机组成实验第一关——汉字国标码转区位码实验【详细步骤版】
  18. UML介绍及怎么看UML图
  19. 数据仓库搭建DWT层
  20. UT-Exynos4412 三星ARM四核旗舰开发平台android4.0体验-17板载按键调试

热门文章

  1. windows 下 opencv 3.x 的安装及常见问题的解决
  2. Matlab Tricks(十八)—— 矩阵间元素距离的计算
  3. IEEE 各项协议、RFC 文档
  4. Python 标准库 —— fractions
  5. C++基础::limits
  6. jsp模糊查询_[内附完整源码和文档] 基于JSP+Servlet校园二手交易平台
  7. python和java哪个好找工作-Python和java哪个就业前景好些?
  8. python和c++哪个好-C#、C++、Java、Python 选择哪个好?
  9. python自动化办公要学多久-基于python实现自动化办公学习笔记
  10. 知乎热议的Deep Peak2模型究竟是什么?答案都在这里