最长公共子序列(LCS)和最长公共子串(LCS)
2015-12-23 19:13
330 查看
最长公共子序列(Longest Common Subsequence)和最长公共子串( Longest
Common Substring),虽然它们都简称为LCS,但含义不同,前者是可以不连续的,而后者要求是一个连续的字符串。比如:
X="bdcaba";
Y="abcbdab"
X和Y的Longest Common Sequence为<b, c, b, a>,长度为4
X和Y的Longest Common Substring为 <b, d>长度为2
有时候,两个字符串的LCS(不管是哪种LCS)可能不止一个。
1. 动态规划法(DP)求最长公共子序列(Longest Common Sequence)。
.设X,Y的长度分别为m,n。
.定义二维数组dp,用来保存公共子序列的长度,初始化DP数组dp[m]
为0。
.因为当任何一个数组长度为0时,是不存在LCS的,所以dp[0][0...n-1]和dp[0...m-1][0]的值都是0,也就是二维数组的第一列和第一行都为0.
.当X[i]=Y[j]时,则dp[i][j] = dp[i-1][j-1] + 1。1<i<m, 1<j<n。
.当X[i] != Y[j]时,则dp[i][j] = max{dp[i-1][j], dp[i][j-1]}
.等到将dp数组填满时,dp[m]
是值就是LCS的长度--没错,不管两串最后一个字符相不相等。贴一张网上的图:
![](http://img.blog.csdn.net/20151223190332861?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
这里给出自己实现的代码,还是比较简洁、全面的!结果能输出所有的LCS。
2. 动态规划法(DP)求最长公共子串(Longest Common Substring)。
求最长公共子串可以用暴力法,求解思路编码也很清晰。
1 X[i] == Y[j],dp[i][j] = dp[i-1][j-1] + 1
2 X[i] != Y[j],dp[i][j] = 0
这里填满dp二维数组后的情形应该是一组“左上-右下”的一串递增的数字,其它值都为0.
代码如下:
这里有n个字符串最长公共子序列的解法,有兴趣可以研究下,用递归方法调用动态规划法求解,本质还是两个字符串的LCS动态规划解法,在实现时使用了一些小技巧,有点抽象。
Common Substring),虽然它们都简称为LCS,但含义不同,前者是可以不连续的,而后者要求是一个连续的字符串。比如:
X="bdcaba";
Y="abcbdab"
X和Y的Longest Common Sequence为<b, c, b, a>,长度为4
X和Y的Longest Common Substring为 <b, d>长度为2
有时候,两个字符串的LCS(不管是哪种LCS)可能不止一个。
1. 动态规划法(DP)求最长公共子序列(Longest Common Sequence)。
.设X,Y的长度分别为m,n。
.定义二维数组dp,用来保存公共子序列的长度,初始化DP数组dp[m]
为0。
.因为当任何一个数组长度为0时,是不存在LCS的,所以dp[0][0...n-1]和dp[0...m-1][0]的值都是0,也就是二维数组的第一列和第一行都为0.
.当X[i]=Y[j]时,则dp[i][j] = dp[i-1][j-1] + 1。1<i<m, 1<j<n。
.当X[i] != Y[j]时,则dp[i][j] = max{dp[i-1][j], dp[i][j-1]}
.等到将dp数组填满时,dp[m]
是值就是LCS的长度--没错,不管两串最后一个字符相不相等。贴一张网上的图:
这里给出自己实现的代码,还是比较简洁、全面的!结果能输出所有的LCS。
#include<iostream> using namespace std; #define MAX_LEN 20 int dp[MAX_LEN + 1][MAX_LEN + 1]; char chX[MAX_LEN], chY[MAX_LEN]; char chLCS[MAX_LEN]; //保存输出结果 void display_LCS(int i, int j, int idx) { if (i == 0 || j == 0) { cout << chLCS; cout << endl; return; } if (chX[i - 1] == chY[j - 1] && dp[i][j] == dp[i - 1][j - 1] + 1) { chLCS[idx--] = chX[i - 1]; display_LCS(i-1, j-1, idx); } else if (chX[i - 1] != chY[j - 1]) { if(dp[i][j] == dp[i][j - 1]) display_LCS(i, j-1, idx); if(dp[i][j] == dp[i - 1][j]) //注意这里不是else if,所以才能区分出所有情况 display_LCS(i-1, j, idx); } } int main() { cout << "Input two string:" << endl; cin.getline(chX, MAX_LEN); cin.getline(chY, MAX_LEN); size_t i, j; size_t xlen = strlen(chX); size_t ylen = strlen(chY); for (i = 1; i <= xlen; i++) { for (j = 1; j <= ylen; j++) { if (chX[i - 1] == chY[j - 1]) //比较的两字符相等时 dp[i][j] = dp[i - 1][j - 1] + 1; else if (dp[i - 1][j] > dp[i][j - 1]) //比较的两字符不等时,取LCS值较大的。 dp[i][j] = dp[i - 1][j]; else dp[i][j] = dp[i][j - 1]; } } cout << "Length of LCS is: " << dp[xlen][ylen] << endl; cout << "LCS(s): "; //下面是网上普遍的写法,只能输出一种结果 i = xlen; j = ylen; int idx = dp[xlen][ylen]; while (i>0 && j>0) { if (chX[i-1] == chY[j-1] && dp[i][j] == dp[i - 1][j - 1] + 1) { chLCS[--idx] = (chX[i-1]); --i; --j; } else if (/*chX[i-1] != chY[j-1] && */dp[i][j] == dp[i][j - 1]) --j; else --i; } cout << chLCS << endl; //采用递归方法,输出所有的LCS display_LCS(xlen, ylen, dp[xlen][ylen]-1); }
2. 动态规划法(DP)求最长公共子串(Longest Common Substring)。
求最长公共子串可以用暴力法,求解思路编码也很清晰。
int maxlen; /* 记录最大公共子串长度 */ int maxidx; /* 记录最大公共子串在串1的起始位置 */ int compare(char * p, char * q) { int len = 0; while (*p && *q && *p++ == *q++) { ++len; } return len; } void LCS_base(char * X, int xlen, char * Y, int ylen) { for (int i = 0; i < xlen; ++i) { for (int j = 0; j < ylen; ++j) { int len = compare(&X[i], &Y[j]); if (len > maxlen) { maxlen = len; maxidx = i; } } } string out(&X[maxidx], &X[maxidx + maxlen]); cout << "LCS: " << out << endl; }求最长公共子串同样可以用DP的方法,方法与上面相似,但求最长公共子串最大的特点是要求连续,所以重要的一点就是只要当X[i] != Y[j]时,dp[i][j] 就要设为0,和dp[i-1][j]与dp[i][j-1]的值无关。状态转移方程:
1 X[i] == Y[j],dp[i][j] = dp[i-1][j-1] + 1
2 X[i] != Y[j],dp[i][j] = 0
这里填满dp二维数组后的情形应该是一组“左上-右下”的一串递增的数字,其它值都为0.
代码如下:
void LCS2(char *X, size_t xlen, char *Y, size_t ylen) { int maxlen = 0; int maxidx = 0; for (size_t i = 0; i < xlen; i++) { for (size_t j = 0; j < ylen; j++) { if (X[i] == Y[j]) { if (i == 0 || j == 0) dp[i][j] = 1; else dp[i][j] = dp[i - 1][j - 1] + 1; if (maxlen < dp[i][j]) { maxlen = dp[i][j]; maxidx = i - maxlen + 1; } } } } string out(&X[maxidx], &X[maxidx + maxlen]); cout << "LCS: " << out << endl; }同样,LCS结果可能有多个,这里只输出了一个。不过,根据上面描述的dp二维数组的特点,很容易将所有结果输出出来。略
这里有n个字符串最长公共子序列的解法,有兴趣可以研究下,用递归方法调用动态规划法求解,本质还是两个字符串的LCS动态规划解法,在实现时使用了一些小技巧,有点抽象。
相关文章推荐
- IOS学习 CGContextRef学习
- objective-c 中三种产生随机数的方法
- java中volatile关键字的含义
- 在NSString NSNumber NSValue 中一些基本方法的使用和解释
- 按钮点击与弹出时显示不同的图片
- Extjs Widget
- linux软件名规则
- Xcode工程文件打不开:cannot be opened because the project file cannot be parsed
- gdb设置core dump的总结
- QQ好友列表向左滑动出现置顶、删除--第三方开源--SwipeMenuListView
- 残留的linux相关笔记
- Makefile基础
- Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift
- 左右滑动删除ListView条目Item--第三方开源--SwipeToDismiss
- LeetCode 034
- 各种排序算法比较
- jstl中格式化时间戳
- 【AC自动机+DP】[USACO JAN2012 GOLD Problem 1: Video Game Combos]
- CoreAnimation编程指南
- Java集合类之Set