题目
最长公共子序列,给出两个字符串,求出最长公共子序列的长度
比如abcd和becd,最长公共子序列是bcd,长度为3
样例输入
4 4
abcd
becd
输入为长度分别为4和4的字符串。
样例输出
3
abcd和becd的公共部分是bcd,长度为3.
算法
用典型的动态规划就能求解,非常简单,不做解释,直接看dp数组:
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]是
s
1
~
s
i
s_1~s_i
s1~si和
t
1
~
t
j
t_1~t_j
t1~tj的LCS的长度,
d
p
[
0
]
[
.
.
]
dp[0][..]
dp[0][..]和
d
p
[
.
.
]
[
0
]
dp[..][0]
dp[..][0]初始化都为0,递推式如下:
d
p
[
i
+
1
]
[
j
+
1
]
=
{
d
p
[
i
]
[
j
]
+
1
,
s
i
+
1
=
=
t
j
+
1
m
a
x
(
d
p
[
i
+
1
]
[
j
]
,
d
p
[
i
]
[
j
+
1
]
)
,
o
t
h
e
r
w
i
s
e
dp[i+1][j+1]=\left\{ \begin{aligned} dp[i][j]+1, s_{i+1}==t_{j+1}\\ max(dp[i+1][j],dp[i][j+1]), otherwise \end{aligned} \right.
dp[i+1][j+1]={dp[i][j]+1,si+1==tj+1max(dp[i+1][j],dp[i][j+1]),otherwise
再定义一个数组存放LCS,只要 d p [ i + 1 ] [ j + 1 ] = d p [ i ] [ j ] + 1 dp[i+1][j+1]=dp[i][j]+1 dp[i+1][j+1]=dp[i][j]+1,则说明 s i + 1 = = t j + 1 s_{i+1}==t_{j+1} si+1==tj+1,那就把 s i + 1 s_{i+1} si+1存入LCS数组。这个操作详见代码。
代码
#include<stdio.h>
#define maxn 1005
#define maxm 1005
int min(int a, int b){
return (a<b)?a:b;
}
int max(int a, int b){
return (a>=b)?a:b;
}
int main(){
int dp[maxm+1][maxn+1], m, n;
int i, j;
char s[maxm], t[maxn];
while(scanf("%d %d", &m, &n)==2){
int len_LCS;
char LCS[min(m, n)];
getchar();
for(i = 0;i < m;i++){
scanf("%c", s+i);
}
getchar();
for(i = 0;i < n;i++){
scanf("%c", t+i);
}
// 初始化dp数组
for(i = 0;i <= m;i++){
dp[i][0] = 0;
}
for(j = 0;j <= n;j++){
dp[0][j] = 0;
}
// 更新dp数组
for(i = 1;i <= m;i++){
for(j = 1;j <= n;j++){
if(s[i-1]==t[j-1]){
dp[i][j] = dp[i-1][j-1] + 1;
}
else{
dp[i][j] = max(dp[i-1][j], dp[i][j-1]);
}
}
}
// 回溯保存LCS
i = m;
j = n;
len_LCS = dp[m][n];
LCS[len_LCS] = '\0';
while(dp[i][j]!=0){
if(s[i-1]==t[j-1]){
LCS[--len_LCS] = s[i-1];
i--;
j--;
}
else{
if(dp[i-1][j]>=dp[i][j-1])i--;
else j--;
}
}
printf("%d\n", dp[4][4]);
printf("%s\n", LCS);
}
return 0;
}