最长公共子序列(LCS)问题 Longest Common Subsequence 与最长公告字串 longest common substr
问题描述:字符序列的子序列是指从给定字符序列中随意地(不一定连续)去掉若干个字符(可能一个也不去掉)后所形成的字符序列。令给定的字符序列X=“x0,x1,…,xm-1”,序列Y=“y0,y1,…,yk-1”是X的子序列,存在X的一个严格递增下标序列<i0,i1,…,ik-1>,使得对所有的j=0,1,…,k-1,有xij=yj。例如,X=“ABCBDAB”,Y=“BCDB”是X的一个子序列。
考虑最长公共子序列问题如何分解成子问题,设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,…,bn-1”的一个最长公共子序列和找出“a0,a1,…,am-1”和“b0,b1,…,bn-2”的一个最长公共子序列,再取两者中较长者作为A和B的最长公共子序列。
求解:
引进一个二维数组c[][],用c[i][j]记录X[i]与Y[j] 的LCS 的长度,b[i][j]记录c[i][j]是通过哪一个子问题的值求得的,以决定搜索的方向。
我们是自底向上进行递推计算,那么在计算c[i,j]之前,c[i-1][j-1],c[i-1][j]与c[i][j-1]均已计算出来。此时我们根据X[i] = Y[j]还是X[i] != Y[j],就可以计算出c[i][j]。
问题的递归式写成:
回溯输出最长公共子序列过程:
算法分析:
由于每次调用至少向上或向左(或向上向左同时)移动一步,故最多调用(m + n)次就会遇到i = 0或j = 0的情况,此时开始返回。返回时与递归调用时方向相反,步数相同,故算法时间复杂度为Θ(m + n)。
http://blog.csdn.net/yysdsyl/article/details/4226630
一切没有code的分析都是耍流氓。。。 上code
void printLCS(string str1, string str2, vector<vector<int> >flag, int idx1, int idx2)
{ if(idx1 == || idx2 == )
return;
if(flag[idx1][idx2] == )
{
printLCS(str1, str2,flag, idx1-, idx2-);
cout << idx1 <<"\t"<< idx2 <<"\t";
cout << str1[idx1-] <<"\t"<<endl;
}
else if(flag[idx1][idx2] == )
printLCS(str1, str2,flag, idx1, idx2-);
else if(flag[idx1][idx2] == )
printLCS(str1, str2,flag, idx1-, idx2);
} int lcs(string str1, string str2)
{
const size_t len1 = str1.size();
const size_t len2 = str2.size(); if(len1 == || len2 == )
return ; int f[len1 + ][len2 + ];
vector<vector<int> >flag;
vector<int> tmp;
tmp.resize(len2+);
for(size_t i = ; i<= len1; i++)
flag.push_back(tmp); //memset(flag,0,sizeof(flag)); // 1: leftup; 2: left; 3: up
for(size_t i = ; i <= len1; i++)
{
f[i][] = ;
}
for(size_t i = ; i <= len2; i++)
{
f[][i] = ;
} for(size_t i = ; i <= len1; i++)
{
for(size_t j = ; j <= len2; j++)
{
if(str1[i-] == str2[j-])
{
f[i][j] = f[i-][j-] + ;
flag[i][j] = ;
}
else
{
f[i][j] = max(f[i][j-], f[i-][j]);
if(f[i][j-] > f[i-][j])
flag[i][j] = ;
else
flag[i][j] = ;
}
}
}
#if 0
for(size_t i = ; i <= len1; i++)
{
for(size_t j = ; j <= len2; j++)
{
//cout << "f["<<j<<"][" <<i<<"]" << f[j][i] <<"\n";
cout << f[i][j] <<"\t";
}
cout << endl;
}
cout << endl;
for(size_t i = ; i <= len1; i++)
{
for(size_t j = ; j <= len2; j++)
{
//cout << "f["<<j<<"][" <<i<<"]" << f[j][i] <<"\n";
cout << flag[i][j] <<"\t";
}
cout << endl;
}
#endif
printLCS(str1, str2, flag, len1, len2);
return f[len1][len2]; }
最长公共字串:
找两个字符串的最长公共子串,这个子串要求在原字符串中是连续的。其实这又是一个序贯决策问题,可以用动态规划来求解。我们采用一个二维矩阵来记录中间的结果。这个二维矩阵怎么构造呢?直接举个例子吧:"bab"和"caba"(当然我们现在一眼就可以看出来最长公共子串是"ba"或"ab")
b a b
c 0 0 0
a 0 1 0
b 1 0 1
a 0 1 0
我们看矩阵的斜对角线最长的那个就能找出最长公共子串。
不过在二维矩阵上找最长的由1组成的斜对角线也是件麻烦费时的事,下面改进:当要在矩阵是填1时让它等于其左上角元素加1。
b a b
c 0 0 0
a 0 1 0
b 1 0 2
a 0 2 0
这样矩阵中的最大元素就是 最长公共子串的长度。
在构造这个二维矩阵的过程中由于得出矩阵的某一行后其上一行就没用了,所以实际上在程序中可以用一维数组来代替这个矩阵。
与Subsequence问题不同的是,Substring问题不光要求下标序列是递增的,还要求每次
递增的增量为1, 即两个下标序列为:
<i, i+1, i+2, ..., i+k-1> 和 <j, j+1, j+2, ..., j+k-1>
类比Subquence问题的动态规划解法,Substring也可以用动态规划解决,令
c[i][j]表示Xi和Yi的最大Substring的长度,比如
X = <y, e, d, f>
Y = <y, e, k, f>
c[1][1] = 1
c[2][2] = 2
c[3][3] = 0
c[4][4] = 1
动态转移方程为:
如果xi == yj, 则 c[i][j] = c[i-1][j-1]+1
如果xi ! = yj, 那么c[i][j] = 0
最后求Longest Common Substring的长度等于
max{ c[i][j], 1<=i<=n, 1<=j<=m}
完整的代码如下:
/**
找出两个字符串的最长公共连续子串的长度
** author :liuzhiwei
** data :2011-08-16
**/
#include "stdio.h"
#include "string.h"
#include "stdlib.h" int longest_common_substring(char *str1, char *str2)
{
int i,j,k,len1,len2,max,x,y;
len1 = strlen(str1);
len2 = strlen(str2);
int **c = new int*[len1+];
for(i = ; i < len1+; i++)
c[i] = new int[len2+];
for(i = ; i < len1+; i++)
c[i][]=; //第0列都初始化为0
for(j = ; j < len2+; j++)
c[][j]=; //第0行都初始化为0
max = -;
for(i = ; i < len1+ ; i++)
{
for(j = ; j < len2+; j++)
{
if(str1[i-]==str2[j-]) //只需要跟左上方的c[i-1][j-1]比较就可以了
c[i][j]=c[i-][j-]+;
else //不连续的时候还要跟左边的c[i][j-1]、上边的c[i-1][j]值比较,这里不需要
c[i][j]=;
if(c[i][j]>max)
{
max=c[i][j];
x=i;
y=j;
}
}
} //输出公共子串
char s[];
k=max;
i=x-,j=y-;
s[k--]='\0';
while(i>= && j>=)
{
if(str1[i]==str2[j])
{
s[k--]=str1[i];
i--;
j--;
}
else //只要有一个不相等,就说明相等的公共字符断了,不连续了
break;
}
printf("最长公共子串为:");
puts(s);
for(i = ; i < len1+; i++) //释放动态申请的二维数组
delete[] c[i];
delete[] c;
return max;
}
int main(void)
{
char str1[],str2[];
printf("请输入第一个字符串:");
gets(str1);
printf("请输入第二个字符串:");
gets(str2);
int len = longest_common_substring(str1, str2);
printf("最长公共连续子串的长度为:%d\n",len);
system("pause");
return ;
}
最长公共子序列(LCS)问题 Longest Common Subsequence 与最长公告字串 longest common substr的更多相关文章
- 动态规划之最长公共子序列LCS(Longest Common Subsequence)
一.问题描述 由于最长公共子序列LCS是一个比较经典的问题,主要是采用动态规划(DP)算法去实现,理论方面的讲述也非常详尽,本文重点是程序的实现部分,所以理论方面的解释主要看这篇博客:http://b ...
- C++版 - Lintcode 77-Longest Common Subsequence最长公共子序列(LCS) - 题解
版权声明:本文为博主Bravo Yeung(知乎UserName同名)的原创文章,欲转载请先私信获博主允许,转载时请附上网址 http://blog.csdn.net/lzuacm. C++版 - L ...
- POJ 1458 Common Subsequence(最长公共子序列LCS)
POJ1458 Common Subsequence(最长公共子序列LCS) http://poj.org/problem?id=1458 题意: 给你两个字符串, 要你求出两个字符串的最长公共子序列 ...
- 编程算法 - 最长公共子序列(LCS) 代码(C)
最长公共子序列(LCS) 代码(C) 本文地址: http://blog.csdn.net/caroline_wendy 题目: 给定两个字符串s,t, 求出这两个字符串最长的公共子序列的长度. 字符 ...
- 1006 最长公共子序列Lcs
1006 最长公共子序列Lcs 基准时间限制:1 秒 空间限制:131072 KB 给出两个字符串A B,求A与B的最长公共子序列(子序列不要求是连续的). 比如两个串为: abcicba abdks ...
- 动态规划----最长公共子序列(LCS)问题
题目: 求解两个字符串的最长公共子序列.如 AB34C 和 A1BC2 则最长公共子序列为 ABC. 思路分析:可以用dfs深搜,这里使用到了前面没有见到过的双重循环递归.也可以使用动态规划,在建 ...
- 51Nod 1006:最长公共子序列Lcs(打印LCS)
1006 最长公共子序列Lcs 基准时间限制:1 秒 空间限制:131072 KB 分值: 0 难度:基础题 收藏 关注 给出两个字符串A B,求A与B的最长公共子序列(子序列不要求是连续的). ...
- 51nod 1006 最长公共子序列Lcs 【LCS/打印path】
1006 最长公共子序列Lcs 基准时间限制:1 秒 空间限制:131072 KB 分值: 0 难度:基础题 收藏 关注 给出两个字符串A B,求A与B的最长公共子序列(子序列不要求是连续的). ...
- 每日一题-——最长公共子序列(LCS)与最长公共子串
最长公共子序列(LCS) 思路: 代码: def LCS(string1,string2): len1 = len(string1) len2 = len(string2) res = [[0 for ...
- 51nod 1006:最长公共子序列Lcs
1006 最长公共子序列Lcs 基准时间限制:1 秒 空间限制:131072 KB 分值: 0 难度:基础题 收藏 关注 给出两个字符串A B,求A与B的最长公共子序列(子序列不要求是连续的). ...
随机推荐
- 关于matplotlib,你要的饼图在这里
Table of Contents 1 官方Demo 2 将实际数据应用于官方Demo 3 一些改善措施 3.1 重新设置字体大小 3.2 设置显示颜色,Method 1: 3.3 设置显 ...
- 【IO】同步、异步、阻塞、非阻塞的理解
最近一直在看跟IO模型有关的内容,感觉差不多理解了,于是开始写这一篇总结博客.针对的操作系统为UNIX/LINUX,大致的体系结构如上图. 操作系统中的客体主要包括了:文件,Socket和进程,本文主 ...
- UML功能模型(用例图)
在UML系统开发中有三个主要的模型:功能模型(从用户角度展示系统的功能,包括用例图).对象模型(采用对象,属性,操作关联等概念展示系统的结构和基础,包括类图.对象图.包图).动态模型(展示系统 ...
- [xsy1232]Magic
题意:一个无向图,每个点有$a_i,b_i$,对任意点$i$你都可以花费$b_i$的费用将$a_i$变为$0$,最后你还要付出$\sum\limits_{i=1}^n\max\limits_{(i,j ...
- bzoj 3790: 神奇项链
3790: 神奇项链 Description 母亲节就要到了,小 H 准备送给她一个特殊的项链.这个项链可以看作一个用小写字 母组成的字符串,每个小写字母表示一种颜色.为了制作这个项链,小 H 购买了 ...
- BUG:upstream timed out (10060: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected
更换Apache扑向Nginx,刚搭建完WNMP,nginx能访问php页面 但是访问现有开发项目报错 [error] 4112#3724: *9 upstream timed out (10060: ...
- Educational Codeforces Round 6 F. Xors on Segments 暴力
F. Xors on Segments 题目连接: http://www.codeforces.com/contest/620/problem/F Description You are given ...
- 获取dataset结果集的第一行第一列字段
DataSet fileNameDs = DbHelper.excuteSqlResultDataSet(strSql); ) { DataTable fileNameDt = fileNameDs. ...
- 【LaTeX】E喵的LaTeX新手入门教程(3)数学公式
昨天熄灯了真是坑爹.前情回顾[LaTeX]E喵的LaTeX新手入门教程(1)准备篇 [LaTeX]E喵的LaTeX新手入门教程(2)基础排版上一期测试答案1.大家一开始想到的肯定是\LaTeX{}er ...
- iOS:图片上传时两种图片压缩方式的比较
上传图片不全面的想法:把图片保存到本地,然后把图片的路径上传到服务器,最后又由服务器把路径返回,这种方式不具有扩展性,如果用户换了手机,那么新手机的沙盒中就没有服务器返回的图片路径了,此时就无法获取之 ...