问题描述
最长公共子序列问题(Longest Common Subsequence problem, LCS)是求两个给定序列X={x1, x2, …, xm}与Y={y1, y2, …, yn}最长公共子序列的问题。
如果序列Z同时为X和Y的子序列,那么Z就称为X与Y的公共子序列。例,设X={a, b, c, b, d, a, b};Y={b, d, c, a, b, a},那么序列{b, c, a}就是X与Y的公共子序列。但是其并不是最长公共子序列,还存在{b, c, b, a},它才是两者的最长公共子序列(之一)。
输入: 给定多组数据。第1行输入数组组数q。接下来的2 * q行输入数据组,每组数据包含X、Y共2个字符串,每个字符串占1行。
输出: 输出每组X、Y的最长公共子序列Z的长度,每个长度占1行。
限制:
1 ≤ q ≤ 150
1 ≤ X、Y的长度 ≤ 1000
若某组数据中X或Y的长度超过100,则q不超过20。
输入示例
3
abcbdab
bdcaba
abc
abc
abc
bc
输出示例
4
3
2
讲解
我们用Xi代表{x1, x2, …, xi},用Yj代表{y1, y2, …, yj}。那么,求长度分别为m、n的两个序列X、Y的LCS,就相当于求Xm与Yn的LCS。我们将其分割为局部问题进行分析。
要考虑以下两种情况。
- x m x_m xm = y n y_n yn时,在 X m − 1 X_{m-1} Xm−1与 Y n − 1 Y_{n-1} Yn−1的LCS后面加上 x m ( = y n ) x_m(=y_n) xm(=yn)就是 X m X_m Xm与 Y n Y_n Yn的LCS。
- x m ≠ y n x_m\neq y_n xm̸=yn时, X m − 1 X_{m-1} Xm−1与 Y n Y_n Yn的LCS和 Y n − 1 Y_{n-1} Yn−1的LCS中更长的一方就是 X m X_m Xm与 Y n Y_n Yn的LCS
这个算法对 X i X_i Xi与 Y j Y_j Yj同样适用。于是准备下述函数解决LCS局部问题。
c[m+1][n+1]该二维数组中,c[i][j]代表 X i X_i Xi与 Y j Y_j Yj 的LCS长度。
c[i][j]的值可由下述递推公式(Recursive Formula)求得
c
[
i
]
[
j
]
=
{
0
i
f
i
=
0
o
r
j
=
0
c
[
i
−
1
]
[
j
−
1
]
+
1
i
f
i
,
j
>
0
a
n
d
x
i
=
y
i
m
a
x
(
c
[
i
]
[
j
−
1
]
,
c
[
i
−
1
]
[
j
]
i
f
i
,
j
>
0
a
n
d
x
i
≠
y
i
c[i][j]= \begin{cases} 0 & if & i=0&or&j=0\\ c[i-1][j-1]+1&if&i,j>0&and&x_i=y_i\\ max(c[i][j-1],c[i-1][j]&if&i,j>0&and&x_i\neq y_i \end{cases}
c[i][j]=⎩⎪⎨⎪⎧0c[i−1][j−1]+1max(c[i][j−1],c[i−1][j]ifififi=0i,j>0i,j>0orandandj=0xi=yixi̸=yi
基于上述变量和公式,可以用动态规划法求序列X与Y的LCS,具体算法如下。
lcs(X, Y)
m = X.length
n = Y.length
for i = 1 to m
c[i][0] = 0
for j = 1 to n
c[0][j] = 0
for i = 1 to m
for j = 1 to n
if X[i] == Y[j]
c[i][j] = c[i-1][j-1] = c[i-1][j-1] + 1
else if c[i-1][j] >= c[i][j-1]
c[i][j] = c[i-1][j]
else
c[i][j] = c[i][j-1]
AC代码如下
#include<iostream>
#include<string>
#include<algorithm>
using namespace std;
static const int N = 1000;
int lcs(string X, string Y){
int c[N + 1][N + 1];
int m = X.size();
int n = Y.size();
int maxl = 0;
X = ' ' + X;//在X[0]中插入空格
Y = ' ' + Y;//在Y[0]中插入空格
for(int i = 1; i <= m; i++) c[i][0] = 0;
for(int j = 1; j <= n; j++) c[0][j] = 0;
for(int i = 1; i <= m; i++){
for(int j = 1; j <= n; j++){
if(X[i] == Y[j]){
c[i][j] = c[i - 1][j - 1] + 1;
} else {
c[i][j] = max(c[i - 1][j], c[i][j - 1]);
}
maxl = max(maxl, c[i][j]);
}
}
return maxl;
}
int main(){
string s1, s2;
int n; cin>>n;
for(int i = 0; i < n; i++){
cin>>s1>>s2;
cout<<lcs(s1, s2)<<endl;
}
return 0;
}