最长公共子序列
算法,最长公共子序列
最长公共子序列(LCS)问题(非连续子序列)的两种解法最长公共子序列也称作最长公共子串,英文缩写是LCS(Longest Common Subsequence)。
其定义是:一个序列S,如果分别是两个或多个已知序列的子序列,且是符合此条件的子序列中最长的,则称S为已知序列的最长公共子序列。
关于子序列的定义通常有两种方式,一种是对子序列没有连续的要求,其子序列的定义就是原序列中删除若干元素后得到的序列。
另一种是对子序列有连续的要求,其子序列的定义是原序列中连续出现的若干个元素组成的序列。
求解子序列是非连续的最长公共子序列问题是一个十分实用的问题,它可以描述两段文字之间的“相似度”,即它们的雷同程度,从而能够用来辨别抄袭。
本文将介绍对子序列没有连续性要求的情况下如何用计算机解决最长公共子序列问题,对子序列有连续性要求的情况下如何用计算机解决最长公共子序列问题将在后续的文章中介绍。
一、动态规划法(Dynamic Programming)最长公共子序列问题应该是属于多阶段决策问题中求最优解一类的问题,凡此类问题在编制计算机程序时应优先考虑动态规划法,如果不能用动态规划法,而且也找不到其它解决方法,还可以考虑穷举法。
对于这个问题,只要能找到描述最长公共子序列的最优子结构和最优解的堆叠方式,并且保证最优子结构中的每一次最优决策都满足“无后效性”,就可以考虑用动态规划法。
使用动态规划法的关键是对问题进行分解,按照一定的规律分解成子问题(分解后的子问题还可以再分解,这是个递归的过程),通过对子问题的定义找出最优子结构中最优决策序列(对于子问题就是最有决策序列的子序列)以及最优决策序列子序列的递推关系(当然还包括递推关系的边界值)。
如果一个给定序列的子序列是在该序列中删去若干元素后得到的序列,也就意味着子序列在原序列中的位置索引(下标)保持严格递增的顺序。
例如,序列S = <B,C,D,B>是序列K = <A,B,C,B,D,A,B>的一个子序列(非连续),序列S的元素在在K中的位置索引I = [2,3,5,7],I是一个严格递增序列。
两个数组的最长公共子序列
两个数组的最长公共子序列最长公共子序列(Longest Common Subsequence, LCS)问题是一种经典的动态规划问题,常常用于比较两个序列的相似性或匹配程度。
在实际应用中,这个问题有着广泛的应用,如字符串匹配、基因序列比对、文本相似性分析等。
为了能够更好地理解和解决最长公共子序列问题,让我们假设有两个数组A和B。
数组A的长度为m,数组B的长度为n。
我们的目标是找到这两个数组中最长的子序列,且该子序列在A和B中的位置相对顺序一致。
简单来说,最长公共子序列是指在两个数组中都存在的一段连续子序列,并且该子序列在两个数组中的相对顺序是一致的。
这个子序列可能不连续,也可能不唯一,但它的长度是最长的。
那么如何求解最长公共子序列呢?动态规划是解决该问题常用的方法。
我们定义一个二维数组dp,其中dp[i][j]表示数组A的前i个元素和数组B的前j个元素的最长公共子序列长度。
接下来就是根据递推关系式来填充这个二维数组。
递推关系式是:当A[i] = B[j]时,dp[i][j] = dp[i-1][j-1] + 1;当A[i] ≠ B[j]时,dp[i][j] = max(dp[i-1][j], dp[i][j-1])。
简单来说,如果当前两个元素相等,那么最长公共子序列的长度就是前一个元素之前的长度加1,如果当前两个元素不相等,那么最长公共子序列的长度就是前一个数组中的最长公共子序列长度,或者是后一个数组中的最长公共子序列长度的较大值。
填充完dp数组后,最长公共子序列的长度就是dp[m][n]。
同时,我们可以通过回溯dp数组来获得最长公共子序列的具体内容。
从dp[m][n]开始,根据当前位置的值和前一个位置的值,逆向回溯,直到回溯到dp[0][0]为止。
最长公共子序列的求解过程可以看作是在两个数组之间寻找相同元素的过程,可以类比为在两条不同的路径上寻找相同的节点。
这个过程可以用于解决许多实际问题,例如寻找相似的DNA序列,找出相同的文章段落等。
最长公共子序列算法
最长公共子序列算法最长公共子序列算法概述最长公共子序列(Longest Common Subsequence,LCS)是一种常见的字符串匹配问题。
给定两个字符串S和T,求它们的最长公共子序列,即在S和T中都出现的最长的子序列。
该问题可以用动态规划算法解决。
算法原理动态规划算法是一种将复杂问题分解成更小的子问题来解决的方法。
在LCS算法中,我们将两个字符串S和T分别看作X和Y,并定义一个二维数组c[i][j]表示X[1..i]和Y[1..j]的LCS长度。
则有以下递推公式:c[i][j] = 0, if i=0 or j=0c[i][j] = c[i-1][j-1]+1, if X[i]=Y[j]c[i][j] = max(c[i-1][j], c[i][j-1]), if X[i]!=Y[j]其中第一行和第一列均初始化为0,因为空字符串与任何字符串的LCS长度均为0。
当X[i]=Y[j]时,说明当前字符相同,那么当前字符可以加入到LCS中,所以LCS长度加1;否则当前字符不能加入到LCS中,则需要从上一个状态继承得到当前状态。
最终结果即为c[m][n],其中m和n分别表示X和Y的长度。
算法实现以下是LCS算法的Python实现:def lcs(X, Y):m = len(X)n = len(Y)c = [[0] * (n+1) for i in range(m+1)]for i in range(1, m+1):for j in range(1, n+1):if X[i-1] == Y[j-1]:c[i][j] = c[i-1][j-1] + 1else:c[i][j] = max(c[i-1][j], c[i][j-1])return c[m][n]其中X和Y分别为两个字符串。
算法优化以上算法的时间复杂度为O(mn),其中m和n分别表示X和Y的长度。
如果X和Y较长,算法会很慢。
但是我们可以通过一些优化来降低时间复杂度。
0011算法笔记——【动态规划】最长公共子序列问题(LCS)
问题描述:一个给定序列的子序列是在该序列中删去若干元素后得到的序列。
确切地说,若给定序列X= { x1, x2,…, x m},则另一序列Z= {z1, z2,…, z k}是X的子序列是指存在一个严格递增的下标序列{i1, i2,…, i k},使得对于所有j=1,2,…,k有X ij=Z j。
例如,序列Z={B,C,D,B}是序列X={A,B,C,B,D,A,B}的子序列,相应的递增下标序列为{2,3,5,7}。
给定两个序列X和Y,当另一序列Z既是X的子序列又是Y的子序列时,称Z是序列X和Y的公共子序列。
例如,若X= { A, B, C, B, D, A, B}和Y= {B, D, C, A, B, A},则序列{B,C,A}是X和Y的一个公共子序列,序列{B,C,B,A}也是X和Y的一个公共子序列。
而且,后者是X和Y的一个最长公共子序列,因为X和Y没有长度大于4的公共子序列。
给定两个序列X= {x1, x2, …, x m}和Y= {y1, y2, … , y n},要求找出X和Y的一个最长公共子序列。
问题解析:设X= { A, B, C, B, D, A, B},Y= {B, D, C, A, B, A}。
求X,Y的最长公共子序列最容易想到的方法是穷举法。
对X的多有子序列,检查它是否也是Y的子序列,从而确定它是否为X和Y的公共子序列。
由集合的性质知,元素为m的集合共有2^m个不同子序列,因此,穷举法需要指数级别的运算时间。
进一步分解问题特性,最长公共子序列问题实际上具有最优子结构性质。
设序列X={x1,x2,……x m}和Y={y1,y2,……y n}的最长公共子序列为Z={z1,z2,……z k}。
则有:(1)若x m=y n,则z k=x m=y n,且z k-1是X m-1和Y n-1的最长公共子序列。
(2)若x m!=y n且z k!=x m,则Z是X m-1和Y的最长公共子序列。
最长公共子序列的最优值和最优解
最长公共子序列的最优值和最优解
最长公共子序列是指两个序列中的最长子序列,该子序列在两个序列中的所有出现位置上都是相同的。
在算法和计算机科学领域,最长公共子序列是一种经常使用的问题,通常用于比较两个字符串的相似程度。
最优值是指在所有可能的解中,最小或最大的值。
在最长公共子序列问题中,最优值通常指两个序列的最长公共子序列长度,也就是最长匹配字符串的长度。
最优解是指符合最优值的解中,最佳的一个。
在最长公共子序列问题中,最优解是指最长公共子序列本身,也就是两个序列中相同的最长子序列。
为了求解最长公共子序列,可以使用动态规划算法。
该算法先将两个序列转换为矩阵形式,然后利用矩阵中的元素来求解最长公共子序列。
最后,根据矩阵中的元素,可以得到最优值和最优解。
总之,最长公共子序列问题是一种常见的算法问题,在计算机科学和信息技术领域经常使用。
最优值和最优解是求解该问题的重要指标,可以帮助我们找到最佳匹配字符串。
- 1 -。
[Python]最长公共子序列VS最长公共子串[动态规划]
[Python]最长公共⼦序列VS最长公共⼦串[动态规划]前⾔由于原微软开源的基于古⽼的perl语⾔的Rouge依赖环境实在难以搭建,遂跟着Rouge论⽂的描述⾃⾏实现。
Rouge存在N、L、S、W、SU等⼏⼤⼦评估指标。
在复现Rouge-L的函数时,便遇到了本博⽂的问题:求两串的最长公共⼦序列。
⼀参考⽂献全⽂参考均如下博⽂。
⼆最长公共⼦序列 & 最长公共⼦串的区别1、最长公共⼦序列(Longest Common Subsequence,LCS):在字符串A和字符串B中都出现的序列,且顺序与母串保持⼀致最长的那个序列。
2、最长公共⼦串(Longest Common Substring):相⽐LCS更加严格,序列必须连续出现,即公共的⼦字符串。
eg: csdnblog与belong,最长公共⼦序列为blog,最长公共⼦串为lo。
三程序设计与实现3.1 最长公共⼦序列def longestCommonSubsequence(seqA, seqB):"""最长公共⼦序列-----------[reference] 最长公共⼦序列与最长公共⼦串【动态规划】 https:///a515557595_xzb/article/details/88296989:param seqA::param seqB::return:"""m = len(seqA);n = len(seqB);init_unit={"len":0,"lcs":[]}dp = [[ init_unit ]*(n+1) for i in range(m+1)]; # m+1⾏, n+1列for i in range(0, m+1):for j in range(0, n+1):if i==0 or j==0:dp[i][j] = init_unit;elif seqA[i-1] == seqB[j-1]:tmp_str = copy.copy((dp[i-1][j-1])["lcs"]);tmp_str.append(seqA[i-1]);unit = {"len": (dp[i-1][j-1])["len"] + 1,"lcs": tmp_str}dp[i][j] = unit;elif seqA[i-1] != seqB[j-1]:if (dp[i-1][j])["len"] > (dp[i][j-1])["len"]: # 存储最长的信息dp[i][j] = dp[i-1][j];else:dp[i][j] = dp[i][j-1];else:pass;pass; # end inner for looppass; # end outer for loopreturn dp[m][n];print( longestCommonSubsequence("GM%$ABG", "gbndGFMABG") ) # {'len': 5, 'lcs': ['G', 'M', 'A', 'B', 'G']}print( longestCommonSubsequence(["G", "M", "%", "$", "A", "B", "G"], ["g","b", "n", "d", "G", "F", "M", "A", "B","G"] ) ); # {'len': 5, 'lcs': ['G', 'M', 'A', 'B', 'G']}3.2 最长公共⼦串def longestCommonSubstring(strA, strB):"""最长公共⼦串-----------[reference] 最长公共⼦序列与最长公共⼦串【动态规划】 https:///a515557595_xzb/article/details/88296989:param strA::param strB::return:"""m = len(strA);n = len(strB);init_unit={"len":0,"lcs":[]}dp = [[ init_unit ]*(n+1) for i in range(m+1)]; # m+1⾏, n+1列result ={"len":0, # 记录最长公共⼦串的长度"lcs": []};for i in range(0, m+1): # 考虑i为0或j为0的情况for j in range(0, n+1):if i==0 or j==0 or ( strA[i-1] != strB[j-1] ):dp[i][j] = init_unit;elif strA[i-1] == strB[j-1]:tmp_str = copy.copy((dp[i-1][j-1])["lcs"]);tmp_str.append(strA[i-1]);unit = {"len": (dp[i-1][j-1])["len"] + 1,"lcs": tmp_str}dp[i][j] = unit;if (dp[i][j])["len"] > result["len"]: # 存储最长的信息result = copy.copy( dp[i][j] );else:pass;pass; # end inner for looppass; # end outer for loopreturn result;print( longestCommonSubstring("GM%$ABG", "gbndGFMABG") ) # {'len': 3, 'lcs': ['A', 'B', 'G']}print( longestCommonSubstring(["G", "M", "%", "$", "A", "B", "G"], ["g","b", "n", "d", "G", "F", "M", "A", "B","G"] ) ); # {'len': 3, 'lcs': ['A', 'B', 'G']}四应⽤领域4.1 机器学习 > ⾃动⽂本摘要 / 机器翻译 / 机器阅读理解等任务中 > 评估指标 > Rouge-LRouge-L分类:句⼦级: 最长公共⼦序列⽂摘级: Union[多条句⼦] 最长公共⼦序列推荐博⽂:推荐论⽂: 《ROUGE: A Package for Automatic Evaluation of Summaries》。
数据结构与算法题解:最长公共子序列和最长公共子串
f [0][0] = 0, f [0][∗] = 0, f [∗][0] = 0,最后应该返回f [lenA][lenB]. 即 f 中索引与字符串串索引
ource, target)); }
}
二二、最⻓长公共子子串串
2.1 简单考虑
可以使用用两根指针索引分别指向两个字符串串的当前遍历位置,若遇到相等的字符时则同时向后移 动一一位。
public class Demo { public static int longestCommonSubstring(String A, String B) { if (A == null || A.length() == 0) return 0; if (B == null || B.length() == 0) return 0; int lenA = A.length(); int lenB = B.length(); int lcs = 0, lcs_temp = 0; for (int i = 0; i < lenA; ++i) { for (int j = 0; j < lenB; ++j) { lcs_temp = 0; while ((i + lcs_temp < lenA) && (j + lcs_temp < lenB) && (A.charAt(i + lcs_temp) == B.charAt(j + lcs_temp))) { ++lcs_temp; }
最长公共子序列的时间复杂度
最长公共子序列的时间复杂度简介最长公共子序列(Longest Common Subsequence,简称LCS)是一个经典的动态规划问题,用于求解两个序列中最长的公共子序列的长度。
在计算机科学中,LCS问题是一个重要的基础问题,被广泛应用于字符串相似度比较、基因序列分析、文本相似度计算等领域。
问题描述给定两个序列X和Y,求解它们的最长公共子序列的长度。
序列是由若干个元素组成的有序集合,子序列是原序列中选择若干个元素并按照原有顺序排列得到的新序列。
例如,序列X={A,B,C,B,D,A,B}和序列Y={B,D,C,A,B,A}的最长公共子序列是{B,C,B,A},长度为4。
动态规划算法最长公共子序列问题可以通过动态规划算法求解。
动态规划算法通常包括两个关键步骤:定义状态和状态转移方程。
定义状态在最长公共子序列问题中,我们定义一个二维数组dp[m+1][n+1]来表示序列X和Y 的最长公共子序列的长度,其中m和n分别为序列X和Y的长度。
数组dp的大小为(m+1)×(n+1)是为了方便处理序列为空的情况。
状态转移方程根据最长公共子序列的定义,我们可以得到如下的状态转移方程:if X[i] == Y[j]:dp[i][j] = dp[i-1][j-1] + 1else:dp[i][j] = max(dp[i-1][j], dp[i][j-1])其中,X[i]表示序列X的第i个元素,Y[j]表示序列Y的第j个元素。
如果X[i]和Y[j]相等,则最长公共子序列的长度等于前一个位置的最长公共子序列的长度加1;如果X[i]和Y[j]不相等,则最长公共子序列的长度等于分别考虑序列X的前i-1个元素和序列Y的前j个元素的最长公共子序列的长度,以及分别考虑序列X的前i个元素和序列Y的前j-1个元素的最长公共子序列的长度中的较大值。
求解过程根据状态转移方程,我们可以通过填表的方式求解最长公共子序列的长度。
首先初始化dp数组的第一行和第一列为0,然后按照从左到右、从上到下的顺序依次计算dp数组的值,最终得到dp[m][n]即为所求的最长公共子序列的长度。
动态规划经典——最长公共子序列问题(LCS)和最长公共子串问题
动态规划经典——最长公共⼦序列问题(LCS)和最长公共⼦串问题⼀.最长公共⼦序列问题(LCS问题)给定两个字符串A和B,长度分别为m和n,要求找出它们最长的公共⼦序列,并返回其长度。
例如: A = "Hel lo W o rld" B = "loo p"则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 ⾏和第 0 列,初始化为 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]=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]代码的实现如下: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;elsedp[i][j] = max(dp[i - 1][j],dp[i][j - 1]);}return dp[n][m];//最终的返回结果就是dp[n][m]}};该算法的时间复杂度为O(n*m),空间复杂度为O(n*m)。
最长公共子序列
最长公共子序列
最长公共子序列(longest common subsequence,简称LCS)是在一系列序列中找到最长
公共子序列的算法,这是一种计算机科学中重要的算法,因为它可以用于文本处理、图像处理和生物信息学中的序列比较,并给定的两个序列的编辑距离。
关于LCS算法的历史,称为最早的提出者是Muntz在1971年提出的,但它后来也被Hirschberg和哈利时用来
计算机科学领域。
LCS算法是一种动态规划方法,它的关键在于它需要从底部开始着手,然后一步步的递推到上面,而不是传统的栈方式,只有从上面开始而不会有正确的结果,同时它具有极高的计算效率。
通过动态规划,我们可以用二维表dp[i] [j]来记录字符串X[0]…X[i] 与Y[0]…Y[j] 的LCS,这是该方法的最重要的步骤,每更新一次状态表dp,就可以获得一个新的解。
此外,我们可以使用空间换时间的思想来优化我们的LCS算法,通常有单行存储和单列存储两种方式,尽管这也大大降低了内存的占用,但在计算量的增加的情况下,这种方式也不是很实用。
最长公共子序列算法已经广泛应用在计算机科学,生物信息和图像处理上,它使得在序列比较或文本处理中的传统办法变得简单,更加可靠。
这种算法有着极高的计算效率,可以加快运算的速度,并使用尽可能少的内存存储状态表,这使得LCS算法在当前的应用中日益受到重视。
最长公共子序列
穷举法:
若要求两个序列X,Y的最长公共子序列, 先取得X,Y的所有子序列,并进行一一 比较,共有如下不同的组合:
Cm1 Cm2 ... Cmm 2m Cn1 Cn2 ... Cnn 2n
共要进行不同的比较:2 m+n
最长公共子序列的结构
设序列X={x1,x2,…,xm}和Y={y1,y2,…,yn}的最长公共子序列为 Z={z1,z2,…,zk} ,则 (1)若xm=yn,则zk=xm=yn,且zk-1是xm-1和yn-1的最长公共子序列。 (2)若xm≠yn且zk≠xm,则Z是xm-1和Y的最长公共子序列。 (3)若xm≠yn且zk≠yn,则Z是X和yn-1的最长公共子序列。
最长公共子序列的定义:
•若给定序列X={x1,x2,…,xm},则另一序列 Z={z1,z2,…,zk},是X的子序列是指存在一个严格递增 下例标如序,列序列{i1,Zi2=,…{B,,ik}C使,得D对,于B所}是有序j=列1,X2=,…{A,,k有B:,zCj=,xiBj。, D,A,B}的子序列,相应的递增下标序列为{2,3,5, 7}。而子串则要求左右两元素在母串中为相邻。 •给定2个序列X和Y,当另一序列Z既是X的子序列又是 Y的子序列时,称Z是序列X和Y的公共子序列。 •给定2个序列X={x1,x2,…,xm}和Y={y1,y2,…,yn},找 出X和Y的最长公共子序列。
算法的改进
•如果只需要计算最长公共子序列的长度,则 算法的空间需求可大大减少。事实上,在计 算c[i][j]时,只用到数组c的第i行和第i-1行。 因此,用2行的数组空间就可以计算出最长公 共子序列的长度。进一步的分析还可将空间 需求减至O(min(m,n))。 •思考题:如何对程序进行改正,作为思考题。
1143.最长公共子序列
1143.最长公共⼦序列题⽬描述给定两个字符串 text1 和 text2,返回这两个字符串的最长公共⼦序列的长度。
如果不存在公共⼦序列,返回 0 。
⼀个字符串的⼦序列是指这样⼀个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。
例如,"ace" 是 "abcde" 的⼦序列,但 "aec" 不是 "abcde" 的⼦序列。
两个字符串的公共⼦序列是这两个字符串所共同拥有的⼦序列。
思路分析动态规划的最长公共⼦序列模板题⽬。
设定dp[i] [j]表⽰两个字符串前i个字符和前j个字符的最长公共⼦序列,那么当text1[i]==text2[j]的时候,就等于dp[i] [j]=dp[i-1] [j-1]+1,当不相等的时候,那么dp[i] [j]=max(dp[i-1] [j],dp[i] [j-1]);本题中为了⽅便讨论边界,计算dp[i] [j]的时候⽐较的是第i-1个字符和第j-1个字符,否则在计算dp[0] [0] 和dp[0] [1] 和dp[1] [0]会越界。
代码实现class Solution {public:int longestCommonSubsequence(string text1, string text2) {int len1=text1.size();int len2=text2.size();vector<vector<int>>dp(len1+1,vector<int>(len2+1,0));for(int i=1;i<=len1;i++){for(int j=1;j<=len2;j++){if(text1[i-1]==text2[j-1])dp[i][j]=dp[i-1][j-1]+1;elsedp[i][j]=max(dp[i-1][j],dp[i][j-1]);}}return dp[len1][len2];}};。
最长公共子序列
核心代码(动态规划的实现):
• • • • • • • • • • • • • for (i=1;i<=m;++i) { for (j=1;j<=n;++j) { c[i][j]=0; if (a[i-1]==b[j-1]) c[i][j]=c[i-1][j-1]+1; else { if (c[i][j]<c[i-1][j]) c[i][j]=c[i-1][j]; if (c[i][j]<c[i][j-1]) c[i][j]=c[i][j-1]; } } }
输入输出
• 输入 • 输入数据有T组测试数据。测试数据的数目 (T)在 输入的第一行给出。每组测试数据有两行:每行 有一个字符串,每个字符串的长度都不超过20。 • 输出 • 每个用例,用一行输出其最大公共子序列的长度, 如果没有公共子序列,则输出0。
算法分析
• 依题意分析得,这是一道典型的动态规划题。 • 易得动态方程: • m[i-1][j-1]+1; (stra[i]==strb[j]); • m[i][j]= • max{m[i-1][j],m[i][j-1]} • 通过这个动态方程即可由局部最优解最终得到全 局应用
题目描述:
LOGO
• 最长公共子序列是一个十分实用的问题,它可以描述两段 文字之间的“相似度”,即它们的雷同程度,从而能够用 来辨别抄袭。对一段文字进行修改之后,计算改动前后文 字的最长公共子序列,将除此子序列外的部分提取出来, 这种方法判断修改的部分,往往十分准确。 • 最长公共子序列也称作最长公共子串(不要求连续),英文 缩写为LCS(Longest Common Subsequence)。其定 义是,一个序列 S ,如果分别是两个或多个已知序列的子 序列,且是所有符合此条件序列中最长的,则 S 称为已知 序列的最长公共子序列。 • 例如,若x=<A,B,C,B,D,A,B>和y=<B,D,C, A,B,A>,则序列<B,C,A>是x和y的一个公共子序列, 序列<B,C,B,A>也是x和y的一个公 共子序列。而且, 后者是x和y的一个最长公共子序列,因为x和y没有长度大 于4的公共子序列。
2 最长公共子序列
不用递归的情况下计算lcs(i, j). 不用递归的情况下计算 . 算法Lcs的时间复杂性显然是 的时间复杂性显然是O(nm) 算法 的时间复杂性显然是
int String::Lcs ( String y ) { int n = Length( ), m = y.Length( ); int lcs[MaxN][MaxM]; // MaxN和MaxM 是已定义的常数 int i, j; for ( i = 0; i <= n; i++) lcs[i][0] = 0; // 初始值 for ( j = 0; j <= m; j++) lcs[0][j] = 0; // 初始值 for ( i = 1; i <= n; i++) for ( j = 1; j <= m; j++) if ( str[i] ==y.str[j] ) lcs[i][j] = lcs[i-1][j-1] + 1; else lcs[i][j] = max(lcs[i-1][j], lcs[i][j-1]); return lcs[n][m]; }
穷举法
对于每一个X 的子序列,验证它是否是 验证它是否是Y 对于每一个 m的子序列 验证它是否是 n的子序 列. Xm有2m个子序列 每个子序列需要o(n)的时间来验证它是否是 n的 的时间来验证它是否是Y 每个子序列需要 的时间来验证它是否是 子序列. 子序列 的第一个字母开始扫描下去,如果不是则从 从Yn的第一个字母开始扫描下去 如果不是则从 第二个开始 运行时间: 运行时间 o(n2m)
作业
String String::Lcs ( String x, String y );
最长公共子序列算法
最长公共子序列算法简介最长公共子序列(Longest Common Subsequence,简称LCS)是一种常见的字符串匹配算法。
它用于寻找两个序列中最长的共有子序列,其中顺序是受限的,即字符必须按顺序出现但不需要连续。
概念在介绍最长公共子序列算法之前,我们先来了解一些相关概念:•序列:一个序列是由一个或多个元素组成,可以是字符、数字或其他类型的数据。
•子序列:给定一个序列,它的子序列是从原序列中选择出来的元素,且相对顺序保持不变。
•公共子序列:给定两个或多个序列,它们的公共子序列是在这些序列中都出现的子序列。
算法思想最长公共子序列算法通过动态规划的方法解决问题。
其思想是将原问题分解为若干个子问题,并通过子问题的最优解来构造原问题的最优解。
算法步骤最长公共子序列算法的主要步骤如下:1.初始化一个二维数组dp,dp[i][j]表示序列A的前i个元素与序列B的前j个元素的最长公共子序列的长度。
2.遍历序列A和序列B,通过如下公式计算dp[i][j]的值:–如果A[i]等于B[j],则dp[i][j] = dp[i-1][j-1] + 1。
–如果A[i]不等于B[j],则dp[i][j] = max(dp[i-1][j], dp[i][j-1])。
3.最终得到的dp[n1][n2]就是序列A和序列B的最长公共子序列的长度。
示例为了更好地理解最长公共子序列算法,我们举一个简单的例子:假设序列A为”ABCDAB”,序列B为”BDCAB”,我们来计算它们的最长公共子序列。
1.初始化dp数组。
A B C D A B0 0 0 0 0 0 0B 0D 0C 0A 0B 02.计算dp数组。
–第一行和第一列都为0,因为一个序列的前0个元素与另一个序列的前任意个元素的最长公共子序列都是0。
–当A[i]等于B[j]时,dp[i][j] = dp[i-1][j-1] + 1。
•当i=1,j=1时,A[i]等于B[j],所以dp[1][1] = dp[0][0]+ 1 = 1。
什么是最长公共子序列?
什么是最长公共子序列?
什么是最长公共子序列(LCS)?
最长公共子序列是指在两个序列中找到的最长的公共子序列,该子序列不需要在原序列中是连续的,但在两个序列中的顺序保持一致。
为了更好地理解最长公共子序列,我们可以通过一个具体的例子来说明。
假设我们有两个序列:ABCBDAB 和 BDCABA。
我们的目标是找到这两个序列的最长公共子序列。
首先,我们可以使用动态规划的方法来解决这个问题。
我们可以创建一个二维数组来存储两个序列之间的最长公共子序列的长度。
数组的行和列分别表示两个序列,数组中的每个元素表示对应位置的最长公共子序列的长度。
接下来,我们可以使用以下递推公式来填充数组:
如果两个序列的当前元素相等,则最长公共子序列的长度为前一个元素的最长公共子序列长度加1。
如果两个序列的当前元素不相等,则最长公共子序列的长度为前一个元素的最长公共子序列长度的最大值。
通过填充数组,我们可以得到最长公共子序列的长度。
然后,我们可以根据填充数组的过程来找到最长公共子序列。
最后,我们可以得到最长公共子序列为BCAB,长度为4。
总结起来,最长公共子序列是指在两个序列中找到的最长的公共子序列,不需要在原序列中是连续的,但在两个序列中的顺序保持一致。
我们可以使用动态规划的方法来解决这个问题,通过填充数组来得到最长公共子序列的长度,并根据填充数组的过程来找到最长公共子序列。
最长公共子序列
问题解决方案
由于应用了滚动数组,那么空间开销就 能够从5001*5001压缩到 2*5001 !!!
POJ 1159
#include<iostream> using namespace std; int max(int a,int b){ return a>b?a:b; } int main(int i,int j){ int n; while(cin>>n) { char* s1=new char[n+1]; char* s2=new char[n+1]; //s1的逆序列 int **dp=new int*[n+1]; //定义二维动态滚动数组 dp[0]=new int[n+1]; dp[1]=new int[n+1]; dp[0][0]=dp[1][0]=0;
分析
怎样求最少字符数? 先求原序列S的逆序列为S' 则最少需要补充的字符数 = 原序列S的长 度 — S和S'的最长公共子串长度
问题解决方案
几种不同的申请空间方法的区别: 1. 静态数组 开销大小为5001*5001的int是铁定超的. 2. 动态数组 单纯的申请动态数组是不能解决这个问题 的,动态数组只能增加空间利用率,但是本题最恶劣 的数组大小还是5001*5001,动态数组是不能改变这个 事实的 3. 滚动数组 这里重点讲一下滚动数组在这个题目中的 应用.滚动数组的目的就是减少空间开销.首先可以在纸 上简单模拟一下DP的转移过程.确定好最少行数或者列 数之后,重点就是在如何进行"滚动"以及如何用表达式 控制这个滚动.对于本题,可以用行数以0--1--0--1的滚动 方式,滚动表达式为i%2和(i-1)%2 ,没错,就是强大 的求余滚动
最长公共子序列
0-1背包问题
问题描述
0-1背包问题可描述为:n个物品和1个背包。对物品i,其价值为
vi,重量为wi,背包的容量为W。如何选取物品装入背包,使背 包中所装入的物品的总价值最大? n 约束条件: (4-7) wi xi W i=1 x {0,1},(1 i n) i 目标函数: (4-8) n
B
D
C
A
B
A
0 0 0 0 0 0 0 0
A B C B D A B
0 0 1 1 1 1 1 1
0 0 1 1 1 2 2 2
0 0 1 2 2 2 2 2
0 1ቤተ መጻሕፍቲ ባይዱ1 2 2 2 3 3
0 1 2 2 3 3 3 4
0 1 2 2 3 3 4 4
从i=7,j=6处向前递推 ,找到X和Y的最长公共子序列为{B,C,B,A}
最长公共子序列问题
基本概念 (1)子序列
给定序列 X={x1, x2, …, xn}、Z={z1, z2, …, zk},若 Z是X的子序列,当且仅当存在一个严格递增的下 标序列 {i1, i2, …, ik},对j∈{1, 2, …, k}有zj=x。 (2)公共子序列 给定序列X和Y,序列Z是X的子序列,也是Y的子序 列,则称Z是X和Y的公共子序列。 (3)最长公共子序列 包含元素最多的公共子序列即为最长公共子序列。
实例构造
【例4-6】给定序列X={A, B, C, B, D, A, B}和Y={B, D, C, A, B, A},求
它们的最长公共子序列。
(1)m=7,n=6,将停止条件填入数组c中,即
c[i][0]=0,c[0][j]=0,其中0≤i≤m,0≤j≤n。 (2)当i=1时,X1={A},最后一个字符为A;Yj的 规模从1逐步放大到6,其最后一个字符分别为B、 D、C、A、B、A; 依此类推,直到i=7。
最长公共子序列
一个给定序列的子序列是在该序列中删去若干元素后得到的序列。
最长公共子序列问题是给定两个序列X={x1,x2...xm}和Y={y1,y2...yn},找出X和Y的最长公共子序列,用动态规划算法可有效的解些问题。
最长公共子序列的结构:设X = { x1 , ... , xm },Y = { y1 , ... , yn }及它们的最长子序列Z = { z1 , ... , zk }则1、若 xm = yn ,则 zk = xm = yn,且Z[k-1] 是 X[m-1] 和 Y[n-1] 的最长公共子序列2、若 xm != yn ,且 zk != xm , 则 Z 是 X[m-1] 和 Y 的最长公共子序列3、若 xm != yn , 且 zk != yn , 则 Z 是 Y[n-1] 和 X 的最长公共子序列子问题的递归结构:当 i = 0 , j = 0 时 , c[i][j] = 0当 i , j > 0 ; xi = yi 时 , c[i][j] = c[i-1][j-1] + 1当 i , j > 0 ; xi != yi 时 , c[i][j] = max { c[i][j-1] , c[i-1][j] }由以上分析,先计算最优值,再构造最长公共子序列,C++源程序如下:#include <stdio.h>#include <string.h>#include"iostream"using namespace std;int length=0; //记录最长公共序列长度//计算最优值void lcs(string x,string y,int **b){int i,j;//获取字符串的长度int m = x.size();int n = y.size();//创建动太二维数组int **c;c = new int*[m+1];for(int i = 0;i < m+1;i++)c[i] = new int[n+1];//初始化数组for (i=1;i<=m;i++) c[i][0] = 0;for (i=1;i<=n;i++) c[0][i] = 0;c[0][0] = 0;//遍历记录最优值,并将标识存在数组b中for (i=1;i<=m;i++)for (j=1;j<=n;j++){if (x[i-1] == y[j-1]){ //xm = yn ,则 zk = xm = yn,且Z[k-1] 是 X[m-1] 和 Y[n-1] 的最长公共子序列c[i][j] = c[i-1][j-1] + 1;b[i][j] = 1;}else if (c[i-1][j] > c[i][j-1]){ //xm != yn ,且 zk != xm , 则 Z 是 X[m-1] 和 Y 的最长公共子序列c[i][j] = c[i-1][j];b[i][j] = 2;}else{ //xm != yn , 且 zk != yn , 则 Z 是 Y[n-1] 和 X 的最长公共子序列c[i][j] = c[i][j-1];b[i][j] = 3;}}//释放空间for(int i = 0;i < m+1;i++)delete c[i];delete c;}//构造最长公共子序列void show(int i,int j,string x,int **b){//递归中止条件if (i==0||j==0)return ;//从b[m][n]开始,用递归依其值在数组b中搜索if (b[i][j]==1){ //xi和yj的最长公共子序列是由x(i-1)和y(j-1)的最长公共子序列在尾部加上xi所得到的子序列show(i-1,j-1,x,b);length++;cout << x[i-1];}else if (b[i][j]==2){ //xi和yi的最长公共子序列与x(i-1)和yj的最长公共子序列相同show(i-1,j,x,b);}else{ //xi和yi的最长公共子序列与xi和y(j-1)的最长公共子序列相同show(i,j-1,x,b);}}int main(){string x;string y;int **b;cout << "输入第一个序列";cin >> x;cout << "输入第二个序列";cin >> y;int m = x.size();int n = y.size();//创建二维动态数组b = new int*[m+1];for(int i = 0;i < m+1;i++)b[i] = new int[n+1];//求解最优值lcs(x,y,b);//构造序列并打印cout << "最长公共序列:";show(m,n,x,b);cout << "\n长度:" << length << endl;//释放空间for(int i = 0;i < m+1;i++)delete b[i];delete b;system("pause");return 0;}。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
应用:打字比赛(规则,中外区别)
SARS病毒 文件比较等等 两个一维事物的比较 定义:与子串的区别 算法:穷举法,动态规划法 代码实现:用C
最长公共子序列的定义:
•若给定序列X={x1,x2,…,xm},则另一序列 Z={z1,z2,…,zk},是X的子序列是指存在一个严格递增 下标序列{i1,i2,…,ik}使得对于所有j=1,2,…,k有:zj=xij。 例如,序列Z={B,C,D,B}是序列X={A,B,C,B, D,A,B}的子序列,相应的递增下标序列为{2,3,5, 7}。而子串则要求左右两元素在母串中为相邻。 •给定2个序列X和Y,当另一序列Z既是X的子序列又是 Y的子序列时,称Z是序列X和Y的公共子序列。 •给定2个序列X={x1,x2,…,xm}和Y={y1,y2,…,yn},找 出X和Y的最长公共子序列。
”; 若当前格与上边一格相同,则画“ ”; 上两者都不符合,从当前格到左上格画“ ” (3)从当前格向箭头方向前进一格,对此格进行(4) 从(m,n) 到 (0,0)的不同路径中,“ ”相对应的格的 元素构成最长公共子序列。
找出最长公共子序列 (bcbd,bcdb,badb)
i j
0 1 2 3 4 5 6 7
}while(i<>0&&j<>0);
printf(“%s”,c+k+1); } 时间复杂度为:m+n=O(n)
算法的改进
•如果只需要计算最长公共子序列的长度,则 算法的空间需求可大大减少。事实上,在计 算c[i][j]时,只用到数组c的第i行和第i-1行。 因此,用2行的数组空间就可以计算出最长公 共子序列的长度。进一步的分析还可将空间 需求减至O(min(m,n))。 •思考题:如何对程序进行改正,作为思考题。
void LCS(char a[],int L[][],int m,int n) { /* 求最长公共子序列 */ int i,j,k; char c[m]; i=m;j=n;k=m; do { if(L[i][j]==L[i-1][j]) i--; else if(L[i][j]==L[i]j-1]) j--; else { c[k]=a[ii]; k--;i--;j--;}
穷举法:
若要求两个序列X,Y的最长公共子序列, 先取得X,Y的所有子序列,并进行一一
比较,共有如下不同的组合:
1 2 m Cm Cm ... Cm 2 m 1 2 n Cn Cn ... Cn 2 n
共要进行不同的比较:2 m+n
最长公共子序列的结构
设序列X={x1,x2,…,xm}和Y={y1,y2,…,yn}的最长公共子序列为 Z={z1,z2,…,zk} ,则 (1)若xm=yn,则zk=xm=yn,且zk-1是xm-1和yn-1的最长公共子序列。 (2)若xm≠yn且zk≠xm,则Z是xm-1和Y的最长公共子序列。 (3)若xm≠yn且zk≠yn,则Z是X和yn-1的最长公共子序列。
0 0 0 0 0 0 0 0 0
1 0 0 1 1 1 1 1 1 b
2 0 0 1 1 1 2 2 2 a
3 0 0 1 2 2 2 2 2 c
4 0 1 1 2 2 2 3 3 d
5 0 1 2 2 3 3 3 4 b
6 0 1 2 2 3 3 4 4 d
d b c b a d b
求最长公共子序列
由此可见,2个序列的最长公共子序列包含了这2个序列的前缀 的最长公共子序列。因此,最长公共子序列问题具有最优子结 构性质。
子问题的递归结构
由最长公共子序列问题的最优子结构性质建立子问题最优值 的递归关系。用c[i][j]记录序列和的最长公共子序列的长度。 其中, Xi={x1,x2,…,xi};Yj={y1,y2,…,yj}。当i=0或j=0时,空序 列是Xi和Yj的最长公共子序列。故此时C[i][j]=0。其它情况下, 由最优子结构性质可建立递归关系如下:
0 c[i][ j ] c[i 1][ j 1] 1 max{ c[i][ j 1], c[i 1][ j ]}
i 0, j 0 i, j 0; xi y j i, j 0; xi y j
计算最长公共子序列的长度
void LCSLength(char x[], char y[],int m,int n) { /* 计算最长公共子序列的长度 */ int L[m][n],i,j; for (i = 0; i <= m; i++) L[i][0] = 0; for (i = 0; i <= n; i++) L[0][i] = 0; for (i = 1; i <= m; i++) for (j = 1; j <= n; j++) { if (x[i]==y[j]) L[i][j]=L[i-1][j-1]+1; else if (L[i-1][j]>= L[i][j-1]) L[i][j]= L[i-1][j]; else L[i][j]= L[i][j-1]; return L[m][n]; }
由于在所考虑的子问题空间中,总共有θ(mn)个不同 的子问题,因此,用动态规划算法自底向上地计算最 优值能提高算法的效率。
举例:填充表格
i j
0
1
2
3
4
5
6
d b c b a d b
0 1 2 3 4 5 6 7 b a c d b d
从表中找出最长公共子序列的方法:
(1)从(m,n) 到 (0,0) (2)若当前格与左边一格相同,则画“