动态规划之最长公共子序列问题 C++实现
原理
在之前的文章当中,作者论述了什么是动态规划,这次,我们来看看,如何用动态规划解决最长公共子序列问题。
这个问题经常运用在判断两种生物的相似度----DNA比对上。对比俩串的方式有很多种,例如如果一个串是另一个的字串,那么可以说两个串是相似的:如果将一个串转换为另一个串的操作很少,那么也可以说这两个串是相似的。另一种衡量俩串 S 1 , S 2 S_1,S_2 S1,S2的相似度方式为:寻找第3个串 S 3 S_3 S3,它的所有元素也都出现在 S 1 S_1 S1和 S 2 S_2 S2中,且在三个串中出现的顺序都相同,但在 S 1 , S 2 S_1,S_2 S1,S2中不要求连续出现。我们将最后一种相似的概念描命名最长公共子序列问题。
其形式化定义如下:给定一个序列
X
=
⟨
x
1
,
x
2
…
,
x
m
⟩
X=\left\langle x_1,x_2\dots,x_m\right\rangle
X=⟨x1,x2…,xm⟩,另一个序列
Y
=
⟨
y
1
,
y
2
,
…
,
y
k
⟩
Y=\left\langle y_1,y_2,\dots,y_k\right\rangle
Y=⟨y1,y2,…,yk⟩满足如下条件时成为
X
X
X的子序列(sunsequence),即存在一严格递增的
X
X
X的下标序列
⟨
i
1
,
i
2
,
…
,
i
k
⟩
\left\langle i_1,i_2,\dots,i_k\right\rangle
⟨i1,i2,…,ik⟩,对所有
j
=
1
,
2
,
…
,
k
j=1,2,\dots,k
j=1,2,…,k满足
i
j
=
z
j
_{i_j}=z_j
ij=zj。例如,
Z
=
⟨
B
,
C
,
D
,
B
⟩
Z=\left\langle B,C,D,B\right\rangle
Z=⟨B,C,D,B⟩是
X
=
⟨
A
,
B
,
C
,
B
,
D
,
A
,
B
⟩
X=\left\langle A,B,C,B,D,A,B\right\rangle
X=⟨A,B,C,B,D,A,B⟩的子序列,对应的下标为
⟨
2
,
3
,
5
,
7
⟩
\left\langle 2,3,5,7\right\rangle
⟨2,3,5,7⟩。
给定一个序列
X
=
⟨
x
1
,
x
2
…
,
x
m
⟩
X=\left\langle x_1,x_2\dots,x_m\right\rangle
X=⟨x1,x2…,xm⟩,对
i
=
1
,
2
,
…
,
k
i=1,2,\dots,k
i=1,2,…,k,定义
X
X
X的第
i
i
i前缀为
X
i
=
⟨
x
1
,
x
2
…
,
x
i
⟩
X_i=\left\langle x_1,x_2\dots,x_i\right\rangle
Xi=⟨x1,x2…,xi⟩。
刻画最长公共子序列的特征
L
C
S
LCS
LCS的最优子结构:令
X
=
⟨
x
1
,
x
2
…
,
x
m
⟩
X=\left\langle x_1,x_2\dots,x_m\right\rangle
X=⟨x1,x2…,xm⟩和
Y
=
⟨
y
1
,
y
2
,
…
,
y
n
⟩
Y=\left\langle y_1,y_2,\dots,y_n\right\rangle
Y=⟨y1,y2,…,yn⟩为两个序列,
Z
=
⟨
z
1
,
z
2
,
…
,
z
k
⟩
Z=\left\langle z_1,z_2,\dots,z_k\right\rangle
Z=⟨z1,z2,…,zk⟩为
X
X
X和
Y
Y
Y的任意
L
C
S
LCS
LCS。
1.如果
x
m
=
y
n
x_m=y_n
xm=yn,则
z
k
=
x
m
=
y
n
z_k=x_m=y_n
zk=xm=yn且
Z
k
−
1
Z_{k-1}
Zk−1是
X
m
−
1
X_{m-1}
Xm−1和
Y
n
−
1
Y_{n-1}
Yn−1的一个
L
C
S
LCS
LCS。
2.如果
x
m
≠
y
n
x_m\neq y_n
xm=yn,则
z
k
≠
x
m
z_k\neq x_m
zk=xm且
Z
Z
Z是
X
m
−
1
X_{m-1}
Xm−1和
Y
Y
Y的一个
L
C
S
LCS
LCS。
3.如果
x
m
≠
y
n
x_m\neq y_n
xm=yn,则
z
k
≠
y
n
z_k\neq y_n
zk=yn且
Z
Z
Z是
X
X
X和
Y
n
−
1
Y_{n-1}
Yn−1的一个
L
C
S
LCS
LCS。
一个递归解
我们定义
c
[
i
,
j
]
c[i,j]
c[i,j]表示
X
i
X_i
Xi和
Y
j
Y_j
Yj的
L
C
S
LCS
LCS的长度。则根据
L
C
S
LCS
LCS问题的最优子结构性质,可得如下公式:
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
j
m
a
x
(
c
[
i
,
j
−
1
]
,
c
[
i
−
1
,
j
]
)
i
f
i
,
j
>
0
a
n
d
x
i
≠
y
j
c[i,j]=\left\{\begin{matrix} 0&if\ i=0\ or\ j=0 \\ c[i-1,j-1]+1&if\ i,j>0\ and\ x_i=y_j \\ max(c[i,j-1],c[i-1,j])&if\ i,j>0\ and\ x_i\neq\ y_j \end{matrix}\right.
c[i,j]=⎩⎨⎧0c[i−1,j−1]+1max(c[i,j−1],c[i−1,j])if i=0 or j=0if i,j>0 and xi=yjif i,j>0 and xi= yj
源代码
#include <iostream>
#include <utility>
#include <vector>
#include <string>
using namespace std;
//ACCGTCGAGTGCGCGGAAGCCGGCCGAA & CTCGTTCGGAATGCCGTTGCTCTGTAAA
string temp_strX = { "#ACCGTCGAGTGCGCGGAAGCCGGCCGAA" }, temp_strY = { "#CTCGTTCGGAATGCCGTTGCTCTGTAAA" };
//Memoized of Lcs
pair<vector<vector<int>>,vector<vector<int>>> Lcs_Length(const string &temp_strX, const string &strY) {
auto temp_m = temp_strX.size() - 1, temp_n = temp_strY.size() - 1;
vector<vector<int>> temp_VecB, temp_VecC;
temp_VecB.resize(temp_m + 1);
temp_VecC.resize(temp_m + 1);
for(auto &i : temp_VecB) {
i.resize(temp_n + 1);
}
for(auto &i : temp_VecC) {
i.resize(temp_n + 1);
}
for(auto i = 1; i <= temp_m; ++i) {
temp_VecC[i][0] = 0;
}
for(auto j = 0; j <= temp_n; ++j) {
temp_VecC[0][j] = 0;
}
for(auto i = 1; i <= temp_m; ++i) {
for(auto j = 1; j <= temp_n; ++j) {
if(temp_strX[i] == temp_strY[j]) {
temp_VecC[i][j] = temp_VecC[i - 1][j - 1] + 1;
temp_VecB[i][j] = -1;
}
else if(temp_VecC[i - 1][j] >= temp_VecC[i][j - 1]) {
temp_VecC[i][j] = temp_VecC[i - 1][j];
temp_VecB[i][j] = -2;
}
else {
temp_VecC[i][j] = temp_VecC[i][j - 1];
temp_VecB[i][j] = -3;
}
}
}
return make_pair(temp_VecC, temp_VecB);
}
//Print
void Print_Lcs(const vector<vector<int>> & temp_VecB, const string &temp_strX, const size_t &i, const size_t &j) {
if(i == 0 || j == 0) {
return;
}
if(temp_VecB[i][j] == -1) {
Print_Lcs(temp_VecB, temp_strX, i - 1, j - 1);
cout << temp_strX[i];
}
else if(temp_VecB[i][j] == -2) {
Print_Lcs(temp_VecB, temp_strX, i - 1, j);
}
else {
Print_Lcs(temp_VecB, temp_strX, i, j - 1);
}
}
int main() {
auto temp_pair = Lcs_Length(temp_strX, temp_strY);
Print_Lcs(temp_pair.second, temp_strX, temp_strX.size() - 1, temp_strY.size() - 1);
return 0;
}