最长公共子序列(详细代码 注释 分析 以及求出最长公共子序列内容方法)

article/2025/9/6 11:33:44

最长公共子序列

文章有些长,希望能够耐心看完,并且对你有帮助,文章是自己看了书之后,总结的,如果有什么错误的地方,欢迎指出。

一些基本的概念:

子序列: 原序列中删除若干个元素得到的序列,即原序列中可以不连续的一段

子串: 原序列中任意个连续的序列元素组成的序列,即原序列中必须连续的一段。

(两者的元素顺序必须和原序列中的顺序一样)

最长公共子序列: 一个序列即是X序列的子序列,也是Y序列的子序列,则该序列称为为X和Y的公共子序列。对于两个序列的公共子序列是不唯一的,因此,最长公共子序列顾名思义就是长度最长的公共子序列。

思路分析:

方一、从最优子结构去考虑

求最长公共子序列长度:

分析:

​ 因为动态规划的题目是满足最优子结构(最优解包含其子问题的最优解)这一基本条件的,因此我们通过分析最优子结构的特征,从而推出最优值的递推式,然后从子问题最优解出发,求解出整个问题的最优解即可。

​ 假设一个最优子结构情况,对于一个序列Z = {z1, z2, …… , zk}是 X = {x1, x2, ……, xm} 和 Y ={y1, y2 , ……, yn}的最长公共子序列,根据对最后一个元素考虑,我们可以分成三种情况来具体讨论:

1)xm = yn = zk

​ 将xm和yn的值作为序列Z的最长公共子序列的最后一个元素,如果去掉xm和yn,那么对于序列Z的最长公共子序列的长度肯定会减小1,即c[i - 1] [j - 1] = c[i] [j] - 1; 因此,当xm = yn时的递推公式是: c[i] [j] = c[i - 1] [j - 1] + 1;

(为什么要将xm和yn值做为最长公共子序列最后一个元素?你想,两个序列相等的元素且都在最后一个,并且和已知最长公共子序列的最后一个元素相等,无论X和Y前面怎么取公共部分,是不是最后这两个元素都可以作为一个公共部分加入到公共子序列的末尾)

2)xm ≠ yn, xm ≠ zk

​ 表明xm肯定不会作为最长公共子序列的最后一个元素,那么我们把xm去掉,对于{x1, x2,……, xm-1} 和 {y1, y2,……, yn -1}的最长公共子序列也还是{z1, z2,……, zk-1}

3)xm ≠ yn, yn ≠ zk

​ 同理2)情况

由1)2)3)得最优值的递推式:

对于c[i] [j]数组:指只取X前i个元素和只取Y前j个元素能够得到的最长公共子序列的值(即存放最优子结构的值)。

由1)可知,如果xi = yj,那么可以从没有xi和yj的最优子结构即c[i - 1] [j - 1]转移过来

即 c[i] [j] = c[i - 1] [j - 1] + 1;

由2)3)可知,如果xi ≠ yj, 那么我们只需要取xi和yj-1最优子结构和xi-1和yj最优子结构的最大长度即可。

即c[i] [j] = max(c[i] [j - 1] , c[i - 1] [j] );

综上可以得出递推式:

请添加图片描述

自底向上求出问题的最优解:

​ 对子结构考虑的最优情况,得出的递推式是对所有求子问题的最优解适用的,通过该递推公式,自底向上计算子问题的最优值,那么最后的答案也肯定是最优的(这也就是动态规划的最优子结构的特征)

代码:

#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 1010;
char s1[N], s2[N];
int len1, len2;
int c[N][N];
int main()
{//最优子结构状态值初始化(当然也可以不写,因为全局变量默认为0)memset(c, 0, sizeof(c));//输入序列s1和s2的长度cin>>len1>>len2;//输入需要求最长公共子序列的序列s1和s2cin>>s1+1>>s2+1;  for(int i = 1; i <= len1; i ++){for(int j = 1; j <= len2; j ++){if(s1[i] == s2[j])c[i][j] = c[i - 1][j - 1] + 1;else c[i][j] = max(c[i - 1][j], c[i][j - 1]);}}cout<<c[len1][len2]<<'\n';return 0;
}

求最长公共子序列内容:

分析:

对于一个最优子结构c[i] [j]的来源一共有三个,分别是

1)c[i] [j] = c[i - 1] [j - 1] + 1;

2)c[i] [j] = c[i - 1] [j] ;

3)c[i] [j] = c[i] [j - 1];

在自底向上计算最优值的时候,我们可以开一个临时的数组b[i] [j]去记录这3个来源,然后根据最值反向追踪最长公共子序列:

1)b[i] [j] = 1, 即表示取了x[i]或y[j]作为最长公共子序列的一部分,我们输出即可。

2)b[i] [j] = 2,即表示当前c[i] [j]的状态来自c[i - 1] [j],我们追踪c[i - 1] [j]

3)b[i] [j] = 3, 即表示当前c[i] [j]的状态来自c[i] [j - 1],我们追踪c[i] [j - 1]

代码:

#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 1010;
char s1[N], s2[N];
int len1, len2;
int c[N][N];
int b[N][N];
void print(int i, int j){if(i == 0 || j == 0)return;if(b[i][j] == 1){print(i - 1, j - 1);cout<<s1[i];}else if(b[i][j] == 2)print(i - 1, j);else print(i, j - 1);
}
int main()
{//最优子结构状态值初始化memset(c, 0, sizeof(c));//输入序列s1和s2的长度cin>>len1>>len2;//输入需要求最长公共子序列的序列s1和s2cin>>s1+1>>s2+1;  for(int i = 1; i <= len1; i ++){for(int j = 1; j <= len2; j ++){if(s1[i] == s2[j]){c[i][j] = c[i - 1][j - 1] + 1;b[i][j] = 1;}else if(c[i - 1][j] >= c[i][j - 1]){c[i][j] = c[i - 1][j];b[i][j] = 2;}else {c[i][j] = c[i][j - 1];b[i][j] = 3;}}}print(len1, len2);// cout<<c[len1][len2]<<'\n';return 0;
}

当然如果你不想浪费空间,你也可以不用这个临时数组b,直接通过c数组判断即可:

#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 1010;
char s1[N], s2[N];
int len1, len2;
int c[N][N];
void print(int i, int j){if(i == 0 || j == 0)return;if(c[i][j] == c[i - 1][j - 1] + 1){print(i - 1, j - 1);cout<<s1[i];}else if(c[i][j] == c[i - 1][j])print(i - 1, j);else print(i, j - 1);
}
int main()
{//最优子结构状态值初始化memset(c, 0, sizeof(c));//输入序列s1和s2的长度cin>>len1>>len2;//输入需要求最长公共子序列的序列s1和s2cin>>s1+1>>s2+1;  for(int i = 1; i <= len1; i ++){for(int j = 1; j <= len2; j ++){if(s1[i] == s2[j])c[i][j] = c[i - 1][j - 1] + 1;else c[i][j] = max(c[i - 1][j], c[i][j - 1]);}}print(len1, len2);//cout<<c[len1][len2]<<'\n';return 0;
}

方二、闫氏DP分析法(从集合的角度考虑):

分析:

很久之前在Acwing上学习后,写的题解(字丑):

我们通过f[i] [j]去表示所有A[1 ~ i]和B[1 ~ j]的公共子序列的集合,其表示的属性是最大值(即最长长度)。

我们通过最长公共子序列是否包含最后两个元素即a[i]和b[j]对这个集合来进行划分,可以分为4种情况:

  1. a[i]和b[j]相等
  2. a[i]和b[j]不相等,a[i]和bx匹配x在j之前
  3. a[i]和b[j]不相等,b[j]和ax匹配x在i之前
  4. a[i]和b[j]不相等,两数都没有匹配

对于f[i] [j] = f[i - 1] [j - 1] + 1覆盖情况1

对于f[i] [j] = max{f[i - 1] [j], f[i] [j - 1]}覆盖情况2 3 4

请添加图片描述
请添加图片描述

代码:

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1e3 + 5;
int n, m;
int f[N][N];
char a[N], b[N];
int main()
{cin >> n;cin >> a + 1;cin >> m;cin >> b + 1;for(int i = 1; i <= n; i ++)f[i][0] = i;for(int j = 1; j <= m; j ++)f[0][j] = j;for(int i = 1; i <= n; i ++){for(int j = 1; j <= m; j ++){int diff;if(a[i] == b[j])diff = 0;else diff = 1;f[i][j] = min({f[i - 1][j] + 1, f[i][j - 1] + 1, f[i - 1][j - 1] + diff});}}// for(int i = 0; i <= n; i ++){//     for(int j = 0; j <= m; j ++){//         cout << f[i][j] << ' ';//     }//     cout << '\n';// }cout << f[n][m] << '\n';return 0;
}
/*
状态表示:
集合:a前i 变成b前j需要的操作
属性:min状态计算:
划分:根据最后一个元素从增、删、改进行划分
//替
1)A[i] == B[j] 不用进行操作
f[i][j] = f[i - 1][j - 1];2)A[i] != B[j] 
f[i][j] = f[i - 1][j - 1] + 1;3)A[i] 增B[j](ai 和 bj - 1 对齐)
f[i][j] = f[i][j - 1] + 1;4)A[i] 删 A[i](ai - 1 和 bj 对齐)
f[i][j] = f[i - 1][j] + 1;状态转移:*///状态表示:f[i][j]是以1~ai的序列和以1~bj的序列的最长的公共子序列长度//当枚举到的两个元素相同时:
//说明可以将该元素加入到两个子序列的最长公共子序列上,因此就直接对于f[i][j]=f[i-1][j-1]+1,表示在1~a[i-1]和
//1~b[j-1]的公共最长子序列的基础上加1,将f[i-1][j-1]状态加一转移给f[i][j]
//因此我们转移公式就是:f[i][j]=f[i-1][j-1]+1//当枚举到的两个元素不同:
//那我们肯定是不能够将他们加入到两个子序列的最长公共子序列上,因此我们要去寻找之前最长的公共子序列长度转移过来
//对于a[i]和b[j]不相等存在3中情况:
//1、a[i]属于a和b的最长公共子序列,a[i]和bx匹配x在j之前,那么我们就要从f[i][j-1]将状态转移过来,此时的状态就
//是到i,j为止最长的公共子序列长度
//2、b[j]属于a和b的最长公共子序列,b[j]和ax匹配x在i之前,那么我们就要从f[i-1][j]将状态转移过来此时的状态就是
//到i,j为止最长的公共子序列长度
//3、a[i]和b[j]都不属于a和b的最长公共子序列,我们应当是从f[i-1][j-1]去将状态转移过来,但对于f[i-1][j]和f[i][j-1]
//是已经将f[i-1][j-1]的状态包括在里面(因为这三者是相等的)
//因此我们转移公式就是:f[i][j]=max(f[i][j-1],f[i-1][j])

http://chatgpt.dhexx.cn/article/S5CX7KTS.shtml

相关文章

蓝桥杯 最长公共子序列

问题描述&#xff1a; 给定两个字符串&#xff0c;寻找这两个字串之间的最长公共子序列。 输入格式 输入两行&#xff0c;分别包含一个字符串&#xff0c;仅含有小写字母。 输出格式 最长公共子序列的长度。 样例输入 abcdgh aedfhb 样例输出 3 样例说明 最长公共子序列为a&…

最长公共子序列 - LCS

最长公共子序列 - LCS 问题描述子序列定义子串定义公共子序列定义最长公共子序列&#xff08;以下简称LCS&#xff09; 动态规划解决子问题划分及依赖关系递推公式 伪代码代码实现复杂度分析 问题描述 子序列定义 给定一个序列X<x1,x2,x3,x4…,xm>&#xff0c;另一个序…

最长公共子序列C++实现

给定两个字符串 text1 和 text2&#xff0c;返回这两个字符串的最长公共子序列的长度。 一个字符串的 子序列 是指这样一个新的字符串&#xff1a;它是由原字符串在不改变字符的相对顺序的情况下删除某些字符&#xff08;也可以不删除任何字符&#xff09;后组成的新字符串。 …

最长公共子序列动态规划c语言,动态规划----最长公共子序列(C++实现)

最长公共子序列 题目描述:给定两个字符串s1 s2 … sn和t1 t2 … tm 。求出这两个字符串的最长公共子序列的长度。字符串s1 s2 … sn的子序列指可以表示为 … { i1 < i2 < … < ik }的序列。 输入样例 2 asdf adfsd 123abc abc123abc 输出样例 3 6 解题思路: 这道题…

动态规划—最长公共子序列

掌握动态规划求解问题的思想&#xff1b;针对不同的问题&#xff0c;会利用动态规划进行设计求解以及时间复杂度分析&#xff0c;并利用JAVA/C/C等编程语言将算法转换为对应的程序上机运行&#xff08;语言自选&#xff09;。 理解两组序列的最长公共子序列问题&#xff0c;能…

求取最长公共子序列

前言 LCS可以描述两段文字之间的“相似度”&#xff0c;即它们的雷同程度&#xff0c;从而能够用来辨别抄袭。另一方面&#xff0c;对一段文字进行修改之后&#xff0c;计算改动前后文字的最长公共子序列&#xff0c;将除此子序列外的部分提取出来&#xff0c;这种方法判断修改…

最长公共子序列长度

求两个字符串的最长公共子序列长度。 输入格式: 输入长度≤100的两个字符串。 输出格式: 输出两个字符串的最长公共子序列长度。 输入样例1: ABCBDAB BDCABA输出样例1: 4输入样例2: ABACDEF PGHIK输出样例2: 0 (1条消息) HBU训练营【动态规划DP】——最长公共子序列长…

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

算法学习——java实现最长公共子序列学习——java实现最长公共子序列的算法&#xff0c; 实验目的&#xff1a; 输入两个同类型的序列&#xff0c;用动态规划的方法计算它们最长的公共子序列的长度和序列。 (推荐教程&#xff1a; Java视频教程 思考&#xff1a; 1.首先&#x…

动态规划解最长公共子序列(LCS)(附详细填表过程)

目录 相关概念 子序列形式化定义&#xff1a; 公共子序列定义&#xff1a; 最长公共子序列&#xff08;以下简称LCS&#xff09;&#xff1a; 方法 蛮力法求解最长公共子序列&#xff1a; 动态规划求解最长公共子序列&#xff1a; 分析规律&#xff1a; 做法&#xff…

最长公共子序列问题 c语言,最长公共子序列问题C语言

最长公共子序列问题C语言 #include#include#includeint num[50][50]{0}; int b[50][50]{0}; int lcsLength(char *x,char *y,int xLen,int yLen); void LCS(int i,int j,char *x); int lcsLength(char *x,char *y,int xLen,int yLen){ int mxLen-1; int nyLen-1; for(int i0;i&…

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

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

python 最长公共子序列长度_python实现最长公共子序列

最长公共子序列python实现&#xff0c;最长公共子序列是动态规划基本题目&#xff0c;下面按照动态规划基本步骤解出来。 1.找出最优解的性质&#xff0c;并刻划其结构特征 序列a共有m个元素&#xff0c;序列b共有n个元素&#xff0c;如果a[m-1]b[n-1]&#xff0c;那么a[:m]和b…

算法:最长公共子序列(输出所有最长公共子序列)

问题描述&#xff1a;给定两个序列&#xff0c;例如 X “ABCBDAB”、Y “BDCABA”&#xff0c;求它们的最长公共子序列的长度。 下面是求解时的动态规划表&#xff0c;可以看出 X 和 Y 的最长公共子序列的长度为4&#xff1a; 输出一个最长公共子序列并不难&#xff08;网上…

动态规划 最长公共子序列 过程图解

1.基本概念 首先需要科普一下,最长公共子序列(longest common sequence)和最长公共子串(longest common substring)不是一回事儿。什么是子序列呢?即一个给定的序列的子序列,就是将给定序列中零个或多个元素去掉之后得到的结果。什么是子串呢?给定串中任意个连续的字符…

算法知识之最长公共子序列问题(动态规划)

最近朋友让帮做个关于动态规划的最长公共子序列的问题,翻看以前的笔记并完成该题后,顺便写这样一篇文章,希望对大家有所帮助,同时也帮助自己回顾该知识点. 一.最长公共子序列的定义 子序列:若给定序列X{x1,x2,…,xm},则另一序列Z{z1,z2,…,zk},是X的子序列是指存在一个严格递…

动态规划篇——最长公共子序列(c++)

引言&#xff1a;最长公共子序列属于动态规划的基础篇&#xff0c;由字符串的最长公共最序列可以引出很多的问题。 最长子序列详细讲解以及练习题目 需要详细讲解教程的可以观看上面链接的文章&#xff0c;以下是自己做的简单的概括。 一、何为最长公共子序列 A和B的公共子序…

最长公共子序列(LCS)

此文全文参考自&#xff1a;https://blog.csdn.net/dq_dm/article/details/45043689&#xff0c;特此感谢&#xff01; 然后自己参考了&#xff1a;http://www.ahathinking.com/archives/115.html 和July的ppt讲义《十分钟搞定LCS》&#xff0c;为表示版权&#xff0c;特地留下…

动态规划——最长公共子序列

先来讲解以下什么是最长公共子序列。最长公共子序列不是最长相同字符串&#xff0c;有点相似但不一样&#xff0c;来举个简单的例子&#xff0c;有字符串s1bcdea&#xff0c;s2abce&#xff0c;最长相同字符串是bc&#xff0c;最大公共部分是2&#xff1b;而最长公共子序列则是…

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

本题与力扣主站1143题相同. 一.问题描述 一个给定序列的子序列是在该序列中删去若干元素后得到的序列。 确切地说&#xff0c;若给定序列X<x1, x2,…, xm>&#xff0c;则另一序列Z<z1, z2,…, zk>是X的子序列是指存在一个严格递增的下标序列 <i1, i2,…, ik&g…

知识图谱本体建模之RDF、RDFS、OWL详解

&#xff08;一&#xff09;知识图谱本体建模之RDF、RDFS、OWL详解 1.语义网体系 知识图谱于2012年由Google提出&#xff0c;并不是新概念&#xff0c;而是由语义网络(Semantic Network)衍生而来。语义网络由相互连接的节点和边组成&#xff0c;节点表示概念或者对象&#xf…