最长公共子序列问题

合集下载

算法,最长公共子序列

算法,最长公共子序列

最长公共子序列(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)问题是指找到多个字符串中最长的公共子序列。

公共子序列是指这些字符串在所有字符串中都以相同的顺序出现,但不要求连续。

例如,对于字符串"ABCD"和"ACDF",其最长公共子序列为"ACD"。

最长公共子序列问题可以通过动态规划来解决。

基本思路是使用一个二维数组dp,其中dp[i][j]表示字符串A的前i个字符和字符串B的前j个字符的最长公共子序列的长度。

首先,初始化dp[0][j]和dp[i][0]为0,表示空字符串与任何字符串的最长公共子序列长度为0。

然后,对于每个字符A[i]和B[j],如果A[i]等于B[j],则dp[i][j] = dp[i-1][j-1] + 1,表示在之前的最长公共子序列长度的基础上加1。

否则,dp[i][j] = max(dp[i-1][j], dp[i][j-1]),表示要么在字符串A的前i-1个字符和字符串B的前j个字符的最长公共子序列的基础上取得,要么在字符串A的前i个字符和字符串B的前j-1个字符的最长公共子序列的基础上取得。

最终,dp[m][n]即为所求,其中m和n分别为字符串A和字符串B的长度。

可以通过追踪dp数组来还原出最长公共子序列,具体方法是从dp[m][n]开始,如果A[i]等于B[j],则该字符是公共字符,将其加入结果序列,然后向左上方移动,即dp[i-1][j-1]。

如果dp[i][j]等于dp[i-1][j],则说明最长公共子序列在A中取得,向上移动,即dp[i-1][j];如果dp[i][j]等于dp[i][j-1],则说明最长公共子序列在B中取得,向左移动,即dp[i][j-1]。

重复上述过程,直到dp[i][j]为0。

数据结构与算法题解:最长公共子序列和最长公共子串

数据结构与算法题解:最长公共子序列和最长公共子串
公共子子序列列数目目,那么接下来试试寻找其状态转移方方程。 从实际例例子子ABCD和EDCA出发,首首先初始化f的⻓长度为字符串串⻓长度加1,那么有
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; }

动态规划经典——最长公共子序列问题(LCS)和最长公共子串问题

动态规划经典——最长公共子序列问题(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)。

最长公共子序列问题

最长公共子序列问题

目录1 前言02 需求分析12.1 任务和要求12.2 运行环境12.3 开发工具13 分析和设计13.1 系统分析及设计思路13.2 主要数据结构及算法23.3 函数流程图24 具体代码实现35 课程设计总结135.1 程序运行结果或预期运行结果135.2 设计结论13参考文献14致谢141 前言若给定序列X={x1,x2,…,x m},则另一序列Z={z1,z2,…,z k},是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的公共子序列。

请使用C语言编程,设计一个有效的算法解决下述问题:给定2个序列X={x1,x2,…,x m}和Y={y1,y2,…,y n},找出X和Y 的最长公共子序列。

2 需求分析2.1任务和要求使用C语言编程,设计一个有效的算法解决下述问题:给定2个序列X={x1,x2,…,x m}和Y={y1,y2,…,y n},找出X和Y的最长公共子序列。

2.2运行环境(1)WINDOWS2000/XP系统(2)Visual C++ 6.0编译环境或TC编译环境2.3开发工具C语言3 分析和设计3.1 系统分析及设计思路设序列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的最长公共子序列。

由此可见,2个序列的最长公共子序列包含了这2个序列的前缀的最长公共子序列。

Pascal最长公共子序列问题

Pascal最长公共子序列问题

考虑最长公共子序列问题如何分解成子问题,设A=“a0,a1,…, am-1”,B=“b0,b1,…,bm-1”,并Z=“z0,z1,…,zk-1” 为它们的最长公共子序列。不难证明有以下性质: (1) 如果am-1=bn-1,则zk-1=am-1=bn-1,且“z0, z1,…,zk-2”是“a0,a1,…,am-2”和“b0,b1,…, bn-2”的一个最长公共子序列; (2) 如果am-1<>bn-1,则若zk-1<>am-1,蕴涵“z0, z1,…,zk-1”是“a0,a1,…,am-2”和“b0,b1,…, bn-1”的一个最长公共子序列; (3) 如果am-1<>bn-1,则若zk-1<>bn-1,蕴涵“z0, z1,…,zk-1”是“a0,a1,…,am-1”和“b0,b1,…, bn-2”的一个最长公共子序列。 这样,在找A和B的公共子序列时,如有am-1=bn-1,则进一步解 决一个子问题,找“a0,a1,…,am-2”和“b0,b1,…, bm-2”的一个最长公共子序列;如果am-1!=bn-1,则要解决两 个子问题,找出“a0,a1,…,am-2”和“b0,b1,…,bn1”的一个最长公共子序列和找出“a0,a1,…,am-1”和“b0, b1,…,bn-2”的一个最长公共子序列,再取两者中较长者作为 A和B的最长公共子序列。
begin assign(input,'lis.in'); reset(input); assign(output,'lis.out'); rewrite(output); readln(n); for i:=1 to n do maxn[i]:=30000; for i:=1 to n do read(num[i]); dp[1]:=1; maxn[1]:=num[1]; e:=1; for i:=2 to n do {动态规划过程} begin k:=find(num[i])+1; if k+1>dp[i] then dp[i]:=k+1; if k+1>e then e:=k+1; if num[i]<maxn[dp[i]] then maxn[dp[i]]:=num[i]; end;

动态规划解最长公共子序列问题

动态规划解最长公共子序列问题

动态规划解最长公共子序列问题动态规划主要针对最优化问题,它的决策是全面考虑不同的情况分别进行决策,,最后通过多阶段决策逐步找出问题的最终解.当各个阶段采取决策后,会不断决策出新的数据,直到找到最优解.每次决策依赖于当前状态,又随机引起状态的转移.一个决策序列就是在变化的状态中产生出来的,故有”动态”的含义.所以,这种多阶段最优化决策解决问题的过程称为动态规划.一问题的描述与分析字符序列的子序列是指从给定字符序列中随意地(不一定连续)去掉若干字符(可能一个也不去掉)后形成的字符序列..令给定的字符序列X=”x0,x1,x2,…xm-1”,序列Y=”y0,y1,…yk-1”是X的子序列,存在X的一个严格递增下标序列i=i0,i1,i2,…ik-1,使得对所有的j=0,1,2,…k-1,有xi=yi。

例如X=“ABCBDAB”,Y=“BCDB”是X的一个子序列。

给定两个序列A和B,称序列Z是A和B公共子序列,是指Z同是A和B的子序列。

求最长公共子序列。

若A的长度为m,B的长度为n,则A的子序列有2*m-1个,B的子序列有2*n-1个。

采用枚举法分别对A和B的所以子序列一一检查,最终求出最长公共子序列。

如此比较次数(2*2n)接近指数阶,当n较大时,算法太耗时,不可取。

所以要全面考虑不同的情况分别进行决策,,最后通过多阶段决策逐步找出问题的最终解.当各个阶段采取决策后,会不断决策出新的数据,直到找到最优解。

二、算法设计(或算法步骤)A=”a0,a1,a2,……am-1”,B=”b0,b1,b2,……bn-1”,且Z=”z0,z1,z2……zk-1”,为她们的最长公共子序列。

不难证明有一下结论:(1)如果am-1=bn-1,则zk-1=am-1=bn-1,且“z0,z1,z2,……zk-2”是“a0,a1,a2,……am-2”和“b0,b1,b2,……bn-2”的一个最长公共子序列;(2)如果am-1!=bn-1,则若zk-1!=am-1,则“z0,z1,z2,……zk-1”是“a0,a1,a2,……am-2”和”b0,b1,b2,……bn-1”的一个最长公共子序列。

2-最长公共子序列问题(无答案)

2-最长公共子序列问题(无答案)

最长公共子序列问题(LCS)(生物信息学中常用算法)子序列的概念:设X=< x1, x2,┅, x m>,若有1≤i1< i2< ┅<i k≤m,使得Z=< z1, z2,┅, z k> = < x i1, x i2,┅, x ik>,则称Z是X的子序列,记为Z<X。

e.g. X=<A,B,C,B,D,A,B>, Z=<B,C,B,A>, 则有Z<X。

公共子序列的概念:设X,Y是两个序列,且有Z<X和Z<Y,则称Z是X和Y 的公共子序列。

最长公共子序列的概念:若Z<X,Z<Y,且不存在比Z更长的X和Y 的公共子序列,则称Z是X和Y 的最长公共子序列,记为Z∈LCS(X , Y)。

最长公共子序列往往不止一个。

e.g. X=<A,B,C,B,D,A,B>, Y=<B,D,C,A,B,A>, 则Z=<B,C,B,A>, Z’=<B,C,A,B>, Z’’=<B,D,A,B>均属于LCS(X , Y) ,即X,Y有3个LCS。

如何找出X和Y的一个最长公共子序列?Brute-force法:列出X的所有长度不超过n(即∣Y∣)的子序列,从长到短逐一进行检查,看其是否为Y的子序列,直到找到第一个最长公共子序列。

由于X共有2m个子序列,故此方法对较大的m没有实用价值。

是否能使用动态规划法?如何用?分析:记X i=﹤x1,…,x i﹥即X序列的前i个字符(1≤i≤m)(前缀)Y j=﹤y1,…,y j﹥即Y序列的前j个字符(1≤j≤n)(前缀)假定Z=﹤z1,…,z k﹥∈LCS(X , Y)。

若x m=y n(最后一个字符相同),则不难用反证法证明:该字符必是X与Y的任一最长公共子序列Z(设长度为k)的最后一个字符,即有z k = x m = y n且显然有Z k-1∈LCS(X m-1 , Y n-1) 即Z的前缀Z k-1是X m-1与Y n-1的最长公共子序列。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

2.3最长公共子序列问题
和前面讲的有所区别,这个问题的不涉及走向。

很经典的动态规划问题。

例题16
最长公共子序列
(lcs.pas/c/cpp)
【问题描述】
一个给定序列的子序列是在该序列中删去若干元素后得到的序列。

确切地说,若给定序列X= < x1, x2,…, xm>,则另一序列Z= < z1, z2,…, zk>是X的子序列是指存在一个严格递增的下标序列< i1, i2,…, ik>,使得对于所有j=1,2,…,k有Xij=Zj
例如,序列Z=是序列X=的子序列,相应的递增下标序列为<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>,则序列是X和Y的一个公共子序列,序列也是X和Y的一个公共子序列。

而且,后者是X和Y的一个最长公共子序列,因为X和Y没有长度大于4的公共子序列。

给定两个序列X= < x1, x2, …, xm>和Y= < y1, y2, … , yn>,要求找出X和Y的一个最长公共子序列。

【输入文件】
输入文件共有两行,每行为一个由大写字母构成的长度不超过200的字符串,表示序列X和Y。

【输出文件】
输出文件第一行为一个非负整数,表示所求得的最长公共子序列的长度,若不存在公共子序列,则输出文件仅有一行输出一个整数0,否则在输出文件的第二行输出所求得的最长公共子序列(也用一个大写字母组成的字符串表示。

【输入样例】
ABCBDAB
BDCBA
【输出样例】
4
BCBA
【问题分析】
这个问题也是相当经典的。

这个题目的阶段很不明显,所以初看这个题目没什么头绪,不像前面讲的有很明显的上一步,上一层之类的东西,只是两个字符串而且互相没什么关联。

但仔细分析发现还是有入手点的:
既然说是动态规划,那我们首先要考虑的就是怎么划分子问题,一般对于前面讲到的街道问题和数塔问题涉及走向的,考虑子问题时当然是想上一步是什么?但这个问题没有涉及走向,也没有所谓的上一步,该怎么办呢?
既然是求公共子序列,也就有第一个序列的第i个字符和第二个序列的第j个字符相等的情况。

那么我们枚第一个序列(X)的字符,和第二个序列(Y)的字符。

显然如果X[i]=Y[j]那么起点是1(下面说的子序列都是起点为1的),长度为i的子序列和长度为j的子序列的最长公共子序列就是长度为i-1和长度为j-1 的子序列中最长的公共子
序列加上X[i]或Y[j]。

那要是不相等呢?
如果不相等,也就是说第一个序列长度为I的子序列和第二个序列中长度为j的子序列的公共子序列中X[i]和Y[j]不同时出现。

也就是说第一个序列长度为i的子序列和第二个序列中长度为j的子序列的公共子序列是第一个序列长度为i的子序列和第二个序列中长度为j-1的子序列或第一个序列长度为i-1的子序列和第二个序列中长度为j的子序列的公共子序列中一个更长的。

给定两个序列
X = { x1 , x2 , ... , xm }
Y = { y1 , y2 , ... , yn }
求X和Y的一个最长公共子序列
举例
X = { a , b , c , b , d , a , b }
Y = { b , d , c , a , b , a }
最长公共子序列为
LSC = { b , c , b , a }
分析:
最长公共子序列问题具有最优子结构性质

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] }
【参考程序】
#include <iostream>
#include <iomanip>
#include <fstream>
using namespace std;
#define max 100
ifstream fin("LCS.IN");
ofstream fout("LCS.OUT");
int total=0;
void LCSLength( int m , int n , char *x , char *y , char *b ) {
int i , j , k;
int c[max][max];
memset(c,0,sizeof(c));
for( i = 1 ; i <= m ; i++ )
{
for( j = 1 ; j <= n ; j++ )
{
if( x[i-1] == y[j-1] )
{
c[i][j] = c[i-1][j-1] + 1;
k = i * ( n + 1 ) + j;
b[k] = '\\';
}
else if( c[i-1][j] >= c[i][j-1] )
{
c[i][j] = c[i-1][j];
k = i * ( n + 1 ) + j;
b[k] = '|';
}
else
{
c[i][j] = c[i][j-1];
k = i * ( n + 1 ) + j;
b[k] = '-';
}
}
}
}
void LCS( int i , int j , char *x , char *b , int width )
{
if( i == 0 || j == 0 ) return;
int k = i * ( width + 1 ) + j;
if( b[k] == '\\' )
{
LCS( i - 1 , j - 1 , x , b , width );
fout<<x[i];
total++;
}
else if( b[k] == '|' )
{
LCS( i - 1 , j , x , b , width );
}
else
{
LCS( i , j - 1 , x , b , width );
}
}
int main()
{
char x[max];
// = { 'a' , 'b' , 'c' , 'b' , 'd' , 'a' , 'b' };
char y[max];
// = { 'b' , 'd' , 'c' , 'a' , 'b' , 'a' };
fin>>x>>y;
int m,n;
m = strlen(x);
n = strlen(y);
char b[max];
memset(b,'0',sizeof(b));
LCSLength( m , n , x , y , b );
LCS( m , n , x , b , n );
fout<<endl;
fout<<total<<endl;
return 0;
}。

相关文档
最新文档