介绍
最长公共子序列(Longest Common Subsequence, LCS)问题 就是在一组序列(通常是 2 个)中找出所有序列的最长公共子序列,它与 最长公共字串(Longest Common Substring) 问题不同,它不要求子序列在原来的序列中是位置连续的。
两个序列的最长公共子序列
用 $S_n
$ 表示 $S
$ 的前 $n
$ 个字符,例如,$S=(\text{AGCA})
$ 的前缀为:
$$ \begin{aligned}
S_0&=()\\
S_1&=(\text{A})\\
S_2&=(\text{AG})\\
S_3&=(\text{AGC})\\
S_4&=(\text{AGCA})
\end{aligned} $$
用 $\texttt{LCS}(X,Y)
$ 表示计算 $X
$ 和 $Y
$ 的最长公共子序列的函数,该函数具有两个性质。
$$ \begin{aligned}
\texttt{LCS}(X\verb|^|a,Y\verb|^|a)&=\texttt{LCS}(X,Y)\verb|^|a\\
\texttt{LCS}(X\verb|^|a,Y\verb|^|b)&=\max(\texttt{LCS}(X\verb|^|a,Y),\texttt{LCS}(X,Y\verb|^|b))\\
\end{aligned} $$
其中符号 $\verb|^|
$ 表示字符串连接,$a,b
$ 表示某个字符且 $a\neq b
$。
所以 $\texttt{LCS}
$ 函数的递推定义如下,$X=(x_1x_2\cdots x_m),Y=(y_1y_2\cdots y_n)
$,$\texttt{LCS}(X_i,Y_i)
$ 表示前缀 $X_i
$ 和 $Y_j
$ 的最长公共子序列,则有:
$$ \texttt{LCS}(X_i,Y_i)=\begin{cases}
\phi&(i=0 \text{ or } j=0)\\
\texttt{LCS}(X_{i-1},Y_{j-1})\verb|^|x_i&(i,j\gt0\text{ and }x_i=y_j)\\
\max(\texttt{LCS}(X_i,Y_{j-1}),\texttt{LCS}(X_{i-1},Y_j))&(i,j\gt0 \text{ and }x_i\neq y_j)
\end{cases} $$
类似问题
最短公共超序列(Shortest Common Supersequence, SCS):$|\texttt{SCS}(X,Y)|=n+m-|\texttt{LCS}(X,Y)|
$。
编辑距离(Edit Distance,即把一个字符串变为另一个字符串需要的最小操作数量)。当只允许插入或者删除字符,或者替换字符的代价是插入或删除的两倍时:$d'(X,Y)=n+m-2\cdot|\texttt{LCS}(X,Y)|
$。
动态规划代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
vector<vector<int>> LCS(string X, string Y) {
// 计算 LCS 的长度
int n = X.size(), m = Y.size();
vector<vector<int>> C(n + 1, vector<int>(m + 1));
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= m; ++j) {
if (X[i - 1] == Y[j - 1]) {
C[i][j] = 1 + C[i - 1][j - 1];
} else {
C[i][j] = max(C[i - 1][j], C[i][j - 1]);
}
}
}
return C;
}
string LCS(vector<vector<int>> &C, string X, string Y, int i, int j) {
// 输出某个 LCS
// C: 动态规划矩阵; i: 初始值为 X.size(); j: 初始值为 Y.size()
if (i == 0 || j == 0) {
return "";
}
if (X[i - 1] == Y[j - 1]) {
return LCS(C, X, Y, i - 1, j - 1) + X[i - 1];
}
if (C[i - 1][j] > C[i][j - 1]) {
return LCS(C, X, Y, i - 1, j);
}
return LCS(C, X, Y, i, j - 1);
}
|
两个序列的最长公共子串
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
int LCSs(string X, string Y) {
int n = X.size(), m = Y.size();
vector<vector<int>> C(n + 1, vector<int>(m + 1));
int c = 0;
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= m; ++j) {
if (X[i - 1] == Y[j - 1]) {
C[i][j] = 1 + C[i - 1][j - 1];
} else {
C[i][j] = 0;
}
c = max(c, C[i][j]);
}
}
return c;
}
|
例题
参考