一、最长公共子序列
一个数列 S,如果分别是两个或多个已知数列的子序列,且是所有符合此条件序列中最长的,则S称为已知序列的最长公共子序列。
最长公共子串(Longest CommonSubstring)
和最长公共子序列(LongestCommon Subsequence,LCS)
的区别:子串是串的一个连续的部分,子序列则是从不改变序列的顺序,而从序列中去掉任意的元素而获得的新序列;更简略地说,子串的字符的位置必须连续,子序列则不必。比如字符串acdfg
同akdfc
的最长公共子串为df
,而他们的最长公共子序列是adf
。
最长公共子序列的结构有如下表示:
设序列
X=<x1, x2, …, xm>
和Y=<y1, y2, …, yn>
的一个最长公共子序列Z=<z1, z2, …, zk>
,则:若
xm=yn
,则zk=xm=yn
且Zk-1
是Xm-1
和Yn-1
的最长公共子序列;
若xm≠yn
且zk≠xm
,则Z
是Xm-1
和Y
的最长公共子序列;
若xm≠yn
且zk≠yn
,则Z
是X
和Yn-1
的最长公共子序列。
其中Xm-1=<x1, x2, …, xm-1>
,Yn-1=<y1, y2, …, yn-1>
,Zk-1=<z1, z2, …, zk-1>
。因此可以根据这个方程来进行填表,以"helloworld"和“loop”为例:
0 h e l l o w o r l d 0 0 0 0 0 0 0 0 0 0 0 0 l 0 0 0 1 1 1 1 1 1 1 1 o 0 0 0 1 1 2 2 2 2 2 2 o 0 0 0 1 1 2 2 3 3 3 3 p 0 0 0 1 1 2 2 3 3 3 3 所以"helloworld"和"loop"的最长公共子序列的长度为3。
代码:
// 最长公共子序列(不连续)
int lcs_length_(const wstring &str1, const wstring &str2) {
if (str1.size() == 0 || str2.size() == 0)
return 0;
int m = str1.size();
int n = str2.size();
// 生成二维容器
vector<vector<int>> dp(m + 1, vector<int>(n + 1));
int i, j;
// 初始化矩阵,第一行和第一列为0,
for (i = 0; i <= m; i++) {
dp[i][0] = 0;
}
for (j = 0; j <= n; j++) {
dp[0][j] = 0;
}
for (i = 1; i <= m; i++) {
for (j = 1; j <= n; j++) {
if (str1[i - 1] == str2[j - 1]) { // xm=yn
dp[i][j] = dp[i - 1][j - 1] + 1;
} else { // xm≠yn
if (dp[i - 1][j] >= dp[i][j - 1])
dp[i][j] = dp[i - 1][j]; // zk≠xm
else
dp[i][j] = dp[i][j-1]; // zk≠yn
}
}
}
return dp[m][n];
}
二、最长公共字串
最长公共子串跟最长公共子序列的唯一区别在于,公共子串要求是连续的,子序列要求不一定连续。
具体的思路还是动态规划,不同点在于动态规划的迭代策略。
和LCS问题唯一不同的地方在于当
xm≠yn
时,最长公共字串就直接等于0了,因为子串必须连续,且最长公共字串表示的是以xm
,yn
截尾的公共子串的长度。因此可以根据这个方程来进行填表,以"helloworld"和“loop”为例
0 h e l l o w o r l d 0 0 0 0 0 0 0 0 0 0 0 0 l 0 0 0 1 1 0 0 0 0 1 0 o 0 0 0 0 0 2 0 1 0 0 0 o 0 0 0 0 0 1 0 1 0 0 0 p 0 0 0 0 0 0 0 0 0 0 0 这个和LCS问题还有一点不同的就是,需要设置一个
MAX
,每一步都更新得到最长公共子串的长度。
代码:
// 最长公共子串(连续)
int lcs2_length_(const wstring &str1, const wstring &str2) {
if (str1.size() == 0|| str2.size() == 0)
return 0;
int m = str1.size();
int n = str2.size();
vector<vector<int>> dp(m + 1, vector<int>(n + 1));
int i, j;
int max = 0;
for (i = 0; i <= m; i++) {
dp[i][0] = 0;
}
for (j = 0; j <= n; j++) {
dp[0][j] = 0;
}
for (i = 1; i <= m; i++) {
for (j = 1; j <= n; j++) {
if (str1[i - 1] == str2[j - 1]) {
dp[i][j] = dp[i - 1][j - 1] + 1;
if (dp[i][j] > max) {
max = dp[i][j]; // 更新最长公共子串
}
}
else {
dp[i][j] = 0;
}
}
}
return max;
}