最长公共子序列

  • 本文讲解的题与leetcode1143.最长公共子序列这题一样,阅读完可以挑战一下。

力扣题目链接

题目叙述:

给定两个字符串,输出其最长公共子序列,并输出它的长度

输入:

ADABEC和DBDCA

输出:

DBC
3

解释

最长公共子序列是DBC,其长度为3

动态规划思路:

  • 我们这题先构建一个模型,我们使用两个指针i,j ,分别用于遍历a字符串,b字符串。如图所示:

  • 然后我们可以设想一个状态变量,也就是一个函数。一个关于两个变量相关的函数,这在代码中体现为二维数组f

  • 然后f[i][j]表示什么呢?表示序列a[1,2,3....i]b[1,2,3....j]的最长公共子序列的长度

状态变量的含义

  • 在这里的状态变量为f[i][j],它的含义是a的前i个字符与b的前j个字符的最长公共子序列的长度

  • 现在就要观察a[i],b[j]是否在当前的最长公共子序列当中。

  • 具体情况如下图:

递推公式:

  • f[i][j]可以分为三种情况讨论,就是:
  1. a[i]b[j]都在最长公共子序列当中,也就是a[i]==b[j]
  2. a[i]!=b[j],并且a[i]不在公共子序列当中。
  3. a[i]!=b[j],并且b[j]不在公共子序列当中。
  • 那我们的递推公式就可与分为两种情况:

    • f[i][j]=f[i-1][j-1]+1a[i]==b[j]
    • f[i][j]=max(f[i-1][j],f[i][j-1])a[i]!=b[j]
  • 显而易见,我们的边界条件为:
    • f[0][j]=0
    • f[i][0]=0
//m是a字符串的长度,n是b字符串的长度
for(int i=1;i<=m;i++){
for(int j=1;j<=n;j++){
//因为我们的f数组是从下标1开始,而字符串是从0开始的下标
if(a[i-1]==b[j-1]) f[i][j]=f[i-1][j-1]+1;
else f[i][j]=max(f[i-1][j],f[i][j-1]);
}
}

遍历顺序

  • 经过上面的分析,明显遍历顺序为i从小到大j也是从小到大

初始化

  • 初始化边界为0即可

举例打印dp数组

  • 如图所示

如何找出对应的最长公共子序列的长度

  • 我们使用p数组来记录每一次f[i][j]的值来源于哪一个方向

    • 1方向代表左上方
    • 2方向代表左方
    • 3方向代表上方
  • 代码改造如下:

for(int i=1;i<=m;i++){
for(int j=1;j<=n;j++){
if(a[i-1]==b[j-1]){
f[i][j]=f[i-1][j-1]+1;
//左上方
p[i][j]=1;
}
else if(f[i-1][j]>f[i][j-1]){
f[i][j]=f[i][j-1];
//左边
p[i][j]=2;
}
else{
f[i][j]=f[i-1][j];
//上边
p[i][j]=3;
}
}
}
  • p[i][j]代表前驱的位置。

算法的执行过程

  • 我们要找到最长公共子序列,只需要找到从结尾开始,往前找到p[i][j]==1,也就是来源于左上方的哪些元素的集合,就是我们的最长公共子序列。(并不是棋盘中所有p[i][j]==1)的元素,而是从右下角出发,往回找到的所有p[i][j]==1的那些元素。
  • 例子如下:

  • 我们使用s数组来储存最长公共子序列

  • 代码实现:

int i,j,k;
char s[200];
i=m;j=n;k=f[m][n];
while(i>0&&j>0){
//左上方
if(p[i][j]==1){
s[k--]=a[i-1];
i--;j--;
}
//左边
else if(p[i][j]==2) j--;
//上边
else i--;
}
for(int i=1;i<=f[m][n];i++) cout<<s[i];

最终代码实现:

#include <iostream>
#include <cstring>
using namespace std; char a[200];
char b[200];
int f[205][205];
int p[205][205];
int m, n; void LCS() {
int i, j;
m = strlen(a);
n = strlen(b); for (i = 1; i <= m; i++) {
for (j = 1; j <= n; j++) {
if (a[i - 1] == b[j - 1]) {
f[i][j] = f[i - 1][j - 1] + 1;
p[i][j] = 1;
}
else if (f[i - 1][j] > f[i][j - 1]) {
f[i][j] = f[i - 1][j];
p[i][j] = 2;
}
else {
f[i][j] = f[i][j - 1];
p[i][j] = 3;
}
}
}
cout << f[m][n] << endl;
}
//寻找出当初的最长公共子序列。
void getLCS() {
int i = m, j = n, k = f[m][n];
char s[200];
s[k] = '\0';
while (i > 0 && j > 0) {
if (p[i][j] == 1) {
s[--k] = a[i - 1];
i--; j--;
}
else if (p[i][j] == 2) {
i--;
}
else {
j--;
}
} cout << s << endl;
} int main() {
cin >> a >> b;
LCS();
getLCS();
return 0;
}

线性dp:最长公共子序列的更多相关文章

  1. hdu1159 dp(最长公共子序列)

    题意:给两个字符串,求这两个字符串的最长公共子序列的长度 因为之前集训的时候做过,所以现在即使会做也并不是什么稀奇的事,依旧为了自己的浅薄感到羞愧啊``` 解法就是通过两个字符串的每个字符互相比较,根 ...

  2. POJ 1159 Palindrome(区间DP/最长公共子序列+滚动数组)

    Palindrome Time Limit: 3000MS   Memory Limit: 65536K Total Submissions: 56150   Accepted: 19398 Desc ...

  3. poj1159--Palindrome(dp:最长公共子序列变形 + 滚动数组)

    Palindrome Time Limit: 3000MS   Memory Limit: 65536K Total Submissions: 53414   Accepted: 18449 Desc ...

  4. 经典dp 最长公共子序列

    首先,说明一下子序列的定义…… 一个序列A={a1,a2,a3,...,an},从中删除任意若干项,剩余的序列叫A的一个子序列. 很明显(并不明显……),子序列……并不需要元素是连续的……(一开始的时 ...

  5. HDU 1159 Common Subsequence【dp+最长公共子序列】

    Common Subsequence Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Other ...

  6. POJ - 1458 Common Subsequence DP最长公共子序列(LCS)

    Common Subsequence A subsequence of a given sequence is the given sequence with some elements (possi ...

  7. hdu 1503:Advanced Fruits(动态规划 DP & 最长公共子序列(LCS)问题升级版)

    Advanced Fruits Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)T ...

  8. POJ1458 Common Subsequence —— DP 最长公共子序列(LCS)

    题目链接:http://poj.org/problem?id=1458 Common Subsequence Time Limit: 1000MS   Memory Limit: 10000K Tot ...

  9. 51nod 1183 编辑距离【线性dp+类似最长公共子序列】

    1183 编辑距离 基准时间限制:1 秒 空间限制:131072 KB 分值: 0 难度:基础题  收藏  关注 编辑距离,又称Levenshtein距离(也叫做Edit Distance),是指两个 ...

  10. 1. 线性DP 1143. 最长公共子序列

    最经典双串: 1143. 最长公共子序列 (LCS)  https://leetcode-cn.com/problems/longest-common-subsequence/submissions/ ...

随机推荐

  1. 高通与At指令:基础概念

    背景 在某个新基线上移植AT指令,发现有问题,因此收集了这个系列的 文章 作为 这方面的知识补充. 原文作者:laozhuxinlu,本文有删改. AT指令在产线中是一类比较重要的问题, 一天没来得及 ...

  2. QT学习:00 介绍

    --- title: framework-cpp-qt-00-介绍 date: 2020-04-08 15:41:54 categories: tags: - c/c++ - qt --- 章节描述: ...

  3. 三屏异显案例分享,基于全国产RK3568J工业平台!

    在工业领域中,能否更灵活.更高效地在主屏幕进行主要任务,并在其他副屏幕上进行其他次要任务(例如查看参考资料.监控其他应用程序),一直都是许多工业领域客户面临的刚需,而"多屏异显"功 ...

  4. IgH EtherCAT主站开发案例分享——基于NXP i.MX 8M Mini

    前  言 本文档主要演示NXP i.MX 8M Mini工业开发板基于IgH EtherCAT控制伺服电机.   演示板卡是创龙科技的TLIMX8-EVM工业开发板,它是基于NXP i.MX 8M M ...

  5. VulnHub_DC-1渗透流程

    DC-1 DC-1 是一个专门建造的易受攻击的实验室,目的是在渗透测试领域获得经验. 它旨在为初学者提供挑战,但它的难易程度取决于您的技能和知识,以及您的学习能力. 要成功完成此挑战,您将需要 Lin ...

  6. node.js (原生模板引擎模板)

    app01 // 引入http模块 const http = require('http'); //连接数据库 require('./model/connects'); // 创建网站服务器 cons ...

  7. [oeasy]python0135_命名惯用法_name_convention

    命名惯用法 回忆上次内容 上次 了解了isidentifier的细节 关于 关键字 关于 下划线   如何查询 变量所指向的地址? id   如何查询 已有的各种变量? locals   如果 用一个 ...

  8. oeasy教您玩转vim - 12 - # 词头词尾

    词头词尾 回忆上节课内容 我们这次学了向前一个单词 w 意思是 word 还学习了向后一个单词 b 意思是 backward 这俩命令都落在单词的第一个字母 还有什么好玩的命令吗? 动手练习 我们可以 ...

  9. 免费的壁纸API

    最近在给EasyDesktop增加背景的功能, 考虑自己在服务器上部署壁纸, 维护成本比较高, 就找了几个免费的壁纸接口, 体验还不错, 这里简单记录一下. 1.Bing壁纸API 地址是: http ...

  10. [rCore学习笔记 010]基于 SBI 服务完成输出和关机

    RustSBI的两个职责 它会在计算机启动时进行它所负责的环境初始化工作,并将计算机控制权移交给内核 在内核运行时响应内核的请求为内核提供服务 这里用不太确切的话表述一下,RustSBI作为介于内核和 ...