问题描述
一个给定序列的子序列是在该序列中删去若干元素后得到的序列。给定两个序列 A A A和 B B B找出两个序列的最长公共子序列。
输入格式
分两行分别输入字符串 A A A和 B B B。
输出格式
输出分为两行,第一行输出一个整数,表示最长公共子序列的长度,第二行以字符串的形式输出最长公共子序列。
输入样例
gksidertefxsxsepbnwb
sdhfirekdjnfdrwesdcxxhi
输出样例
7
sidrexx
解题思路
局部的最长公共子序列一定是全局的最长公共子序列的子集,所以局部的最长公共子序列可以被全局的最长公共子序列进行继承或者拓展。
我们使用
L
C
S
(
P
,
Q
)
LCS(P,Q)
LCS(P,Q)表示序列
P
P
P和
Q
Q
Q的最长公共子序列。现在有两个序列
A
i
=
{
a
1
,
a
2
,
a
3
,
⋯
,
a
i
}
,
B
j
=
{
b
1
,
b
2
,
b
3
,
⋯
,
b
j
}
A_i=\{a_1,a_2,a_3,\cdots,a_i\},B_j=\{b_1,b_2,b_3,\cdots,b_j\}
Ai={a1,a2,a3,⋯,ai},Bj={b1,b2,b3,⋯,bj},当
a
i
=
b
j
=
s
a_i=b_j=s
ai=bj=s时,说明
s
s
s一定是最长公共子序列中的一个元素即
s
∈
L
C
S
(
A
i
,
B
j
)
s\in LCS(A_i,B_j)
s∈LCS(Ai,Bj),并且是对
L
C
S
(
A
i
−
1
,
B
j
−
1
)
LCS(A_{i-1},B_{j-1})
LCS(Ai−1,Bj−1)的一个拓展即
L
C
S
(
A
i
,
B
j
)
=
L
C
S
(
A
i
−
1
,
B
j
−
1
)
∪
{
s
}
LCS(A_i,B_j)=LCS(A_{i-1},B_{j-1})\cup\{s\}
LCS(Ai,Bj)=LCS(Ai−1,Bj−1)∪{s};当
a
i
≠
b
j
a_i\not=b_j
ai=bj时,说明
L
C
S
(
A
i
,
B
j
)
LCS(A_i,B_j)
LCS(Ai,Bj)在
L
C
S
(
A
i
,
B
j
−
1
)
LCS(A_i,B_{j-1})
LCS(Ai,Bj−1)或
L
C
S
(
A
i
−
1
,
B
j
)
LCS(A_{i-1},B_j)
LCS(Ai−1,Bj)中即
L
C
S
(
A
i
,
B
j
)
=
max
(
L
C
S
(
A
i
,
B
j
−
1
)
,
L
C
S
(
A
i
−
1
,
B
j
)
)
LCS(A_i,B_j)=\max(LCS(A_i,B_{j-1}),LCS(A_{i-1},B_j))
LCS(Ai,Bj)=max(LCS(Ai,Bj−1),LCS(Ai−1,Bj))。
通过上面的推理我们知道此问题满足最优子结构和重叠子问题的性质,因此可以使用动态规划解决。由推论可以得出关于计算最长公共子序列长度的状态转移方程:
t
[
i
]
[
j
]
=
{
0
i
=
0
0
j
=
0
t
[
i
−
1
]
[
j
−
1
]
+
1
a
i
=
b
j
max
(
t
[
i
]
[
j
−
1
]
,
t
[
i
−
1
]
[
j
]
)
a
i
≠
b
j
t[i][j]=\left\{ \begin{array}{lrl} 0&&i=0\\ 0&&j=0\\ t[i-1][j-1]+1&&a_i=b_j\\ \max(t[i][j-1],t[i-1][j])&&a_i\not=b_j \end{array} \right.
t[i][j]=⎩⎪⎪⎨⎪⎪⎧00t[i−1][j−1]+1max(t[i][j−1],t[i−1][j])i=0j=0ai=bjai=bj
测试代码
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
using namespace std;
int main(){
string a,b;
cin >> a;
cin >> b;
vector<vector<size_t> > t(a.size()+1,vector<size_t>(b.size()+1,0));///申请内存空间,并将其中全部填入0
for(size_t i=0;i<a.size();i++){
for(size_t j=0;j<b.size();j++){
if(a[i]==b[j]){///如果匹配则对原最长公共子序列进行拓展
t[i+1][j+1] = t[i][j]+1;
}else{///如果不匹配,则在两个序列中找最长的作为当前的最长子公共序列
t[i+1][j+1] = max(t[i][j+1],t[i+1][j]);
}
}
}
cout << t[a.size()][b.size()] << endl;
return 0;
}
#include <iostream>
#include <string>
#include <vector>
#include <stack>
using namespace std;
int main(){
enum direction{
up,left,left_up
};
string a,b;
cin >> a;
cin >> b;
vector<vector<size_t> > t(a.size()+1,vector<size_t>(b.size()+1));///申请内存空间,并将其中全部填入0
vector<vector<direction> > s(a.size()+1,vector<direction>(b.size()+1));///申请内存空间,用于记录寻找最长公共子序列的过程
for(size_t i=0;i<a.size();i++){
for(size_t j=0;j<b.size();j++){
if(a[i]==b[j]){///如果匹配则对原最长公共子序列进行拓展
t[i+1][j+1] = t[i][j]+1;
s[i+1][j+1] = left_up;
}else{///如果不匹配,则在两个序列中找最长的作为当前的最长子公共序列
if(t[i][j+1]<t[i+1][j]){
t[i+1][j+1] = t[i+1][j];
s[i+1][j+1] = left;
}else{
t[i+1][j+1] = t[i][j+1];
s[i+1][j+1] = up;
}
}
}
}
cout << t[a.size()][b.size()] << endl;
stack<char> p;///建立栈
for(size_t i=a.size(),j=b.size();i&&j;){///从最长公共子序列的最后一个元素开始找起直至找到头部
switch(s[i][j]){
case up:
i--;
break;
case left:
j--;
break;
case left_up:
i--;
j--;
p.push(a[i]);///找到匹配项放入栈中
break;
}
}
while(!p.empty()){///弹栈并打印弹出元素
cout << p.top();
p.pop();
}
cout << endl;
return 0;
}