题面
【题目描述】
一个给定序列的子序列是在该序列中删去若干元素后得到的序列。确切地说,若给定序列
X
=
X=
X= {
x
1
,
x
2
,
…
,
x
m
x_1, x_2,…, x_m
x1,x2,…,xm},则另一序列
Z
=
Z=
Z= {
z
1
,
z
2
,
…
,
z
k
z_1, z_2,…, z_k
z1,z2,…,zk}是
X
X
X的子序列是指存在一个严格递增的下标序列 {
i
1
,
i
2
,
…
,
i
k
i_1, i_2,…, i_k
i1,i2,…,ik },使得对于所有
j
=
1
,
2
,
…
,
k
j=1,2,…,k
j=1,2,…,k有
X
i
j
=
Z
j
X_{i_j}=Z_j
Xij=Zj。
例如,序列
Z
=
Z=
Z={
B
,
C
,
D
,
B
B,C,D,B
B,C,D,B}是序列
X
=
X=
X={
A
,
B
,
C
,
B
,
D
,
A
,
B
A,B,C,B,D,A,B
A,B,C,B,D,A,B}的子序列,相应的递增下标序列为{
2
,
3
,
5
,
7
2,3,5,7
2,3,5,7}。给定两个序列
X
X
X和
Y
Y
Y,当另一序列
Z
Z
Z既是
X
X
X的子序列又是
Y
Y
Y的子序列时,称
Z
Z
Z是序列
X
X
X和
Y
Y
Y的公共子序列。例如,若$X= ${
A
,
B
,
C
,
B
,
D
,
A
,
B
A, B, C, B, D, A, B
A,B,C,B,D,A,B}和
Y
=
Y=
Y= {
B
,
D
,
C
,
A
,
B
,
A
B, D, C, A, B, A
B,D,C,A,B,A},则序列{
B
,
C
,
A
B,C,A
B,C,A}是
X
X
X和
Y
Y
Y的一个公共子序列,序列{
B
,
C
,
B
,
A
B,C,B,A
B,C,B,A}也是
X
X
X和
Y
Y
Y的一个公共子序列。而且,后者是
X
X
X和
Y
Y
Y的一个最长公共子序列,因为
X
X
X和
Y
Y
Y没有长度大于
4
4
4的公共子序列。给定两个序列
X
=
X=
X= {
x
1
,
x
2
,
…
,
x
m
x_1, x_2, …, x_m
x1,x2,…,xm}和
Y
=
Y=
Y= {
y
1
,
y
2
,
…
,
y
n
y_1, y_2, … , y_n
y1,y2,…,yn},要求找出
X
X
X和
Y
Y
Y的一个最长公共子序列。
【输入】
输入共两行。每行为一个由大写字母构成的长度不超过
200
200
200的字符串,表示序列
X
X
X和
Y
Y
Y。
【输出】
输出第一行为一个非负整数。表示所求得的最长公共子序列的长度。若不存在公共子序列,则输出仅有一行输出一个整数
0
0
0。
【样例输入】
ABCBDAB
BDCABA
【样例输出】
4
算法分析
状态:
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j] : 字符串
S
S
S 前
i
i
i 个字符与字符串
T
T
T 前
j
j
j 个字符的最长公共子序列的长度。
状态转移方程:
(
1
)
(1)
(1)S[i]$不在公共子序列中,那么字符串
S
S
S 前
i
i
i个 字符与字符串
T
T
T 前
j
j
j 个字符的最长公共子序列的长度
=
=
=字 符串
S
S
S 前
i
−
1
i-1
i−1 个字符与字符串
T
T
T 前
j
j
j 个字符的最长公共子序列的长度:
d
p
[
i
]
[
j
]
=
d
p
[
i
−
1
]
[
j
]
dp[i][j]=dp[i-1][j]
dp[i][j]=dp[i−1][j]
(
2
)
(2)
(2)
T
[
j
]
T[j]
T[j] 不在公共子序列中,那么字符串
S
S
S 前
i
i
i个 字符与字符串
T
T
T 前
j
j
j 个字符的最长公共子序列的长度
=
=
=字 符串
S
S
S 前
i
i
i 个字符与字符串
T
T
T 前
j
−
1
j-1
j−1 个字符的最长公共子序列的长度:
d
p
[
i
]
[
j
]
=
d
p
[
i
]
[
j
−
1
]
dp[i][j]=dp[i][j-1]
dp[i][j]=dp[i][j−1]
(
3
)
(3)
(3)
S
[
i
]
=
T
[
j
]
S[i]=T[j]
S[i]=T[j],将
S
[
i
]
与
T
[
j
]
S[i] 与T[j ]
S[i]与T[j]加入到公共子序列中,那么字符串
S
S
S 前
i
i
i个 字符与字符串
T
T
T 前
j
j
j 个字符的最长公共子序列的长度
=
=
=字 符串
S
S
S 前
i
i
i 个字符与字符串
T
T
T 前
j
−
1
j-1
j−1 个字符的最长公共子序列的长度
+
1
+1
+1:
d
p
[
i
]
[
j
]
=
d
p
[
i
−
1
]
[
j
−
1
]
+
1
dp[i][j]=dp[i-1][j-1]+1
dp[i][j]=dp[i−1][j−1]+1
最后取一个最大值。
边界条件
d
p
[
i
]
[
0
]
=
0
dp[i][0]=0
dp[i][0]=0,字符串
S
S
S 前
i
i
i个 字符与字符串
T
T
T 前
0
0
0 个字符的最长公共子序列的长度为0。
d
p
[
0
]
[
j
]
=
0
dp[0][j]=0
dp[0][j]=0,字符串
S
S
S 前
0
0
0个 字符与字符串
T
T
T 前
j
j
j 个字符的最长公共子序列的长度为0。
定义时,默认为0,就不需要初始化。
参考程序
有两种写法:
1.
1.
1.字符串下标0开始,为了避免i-1,j-1越界,需要分类讨论:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int dp[1100][1100],ans;
char a[1100],b[1100];
int main()
{
gets(a);
gets(b);
int lena=strlen(a);
int lenb=strlen(b);
for(int i=0;i<lena;i++)
for(int j=0;j<lenb;j++)
{
if(i==0&&j==0)
{
if(a[0]==b[0]) dp[0][0]=1;
else dp[0][0]=0;
}
else if(i==0)
{
if(a[0]==b[j]) dp[0][j]=1;
else dp[0][j]=max(dp[0][j],dp[0][j-1]);
}
else if(j==0)
{
if(a[i]==b[0]) dp[i][0]=1;
else dp[i][0]=max(dp[i][0],dp[i-1][0]);
}
else
{
dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
if(a[i]==b[j]) dp[i][j]=max(dp[i][j],dp[i-1][j-1]+1);
}
}
cout<<dp[lena-1][lenb-1]<<endl;
return 0;
}
2. 2. 2.字符串下标1开始:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int dp[1100][1100],ans;
char a1[1100],b1[1100],a[1100],b[1100];
int main()
{
gets(a1);
gets(b1);
int lena=strlen(a1);
int lenb=strlen(b1);
for(int i=1;i<=lena;i++)
a[i]=a1[i-1];
for(int i=1;i<=lenb;i++)
b[i]=b1[i-1];
for(int i=1;i<=lena;i++)
for(int j=1;j<=lenb;j++)
{
dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
if(a[i]==b[j]) dp[i][j]=max(dp[i][j],dp[i-1][j-1]+1);
}
cout<<dp[lena][lenb]-1<<endl;
return 0;
}