本文参考(菜鸟小筑)
LCS问题即最长公共子序列问题,它不要求所求得的字符在所给的字符串中是连续的(例如:输入两个字符串BDCABA和ABCBDAB,字符串BCBA和BDAB都是是它们的最长公共子序列,则输出它们的长度4,并打印任意一个子序列)。
事实上,最长公共子序列问题也有最优子结构性质。
证明LCS的最优子结构:
记:
(字符串X) Xi=﹤x1,⋯,xi﹥即X序列的前i个字符 (1≤i≤m)(前缀)
(字符串Y) Yj=﹤y1,⋯,yj﹥即Y序列的前j个字符 (1≤j≤n)(前缀)
假定(最长公共子序列) Z=﹤z1,⋯,zk﹥ ∈ LCS(X , Y)。
分类讨论:
①
Xm=Yn(最后一个字符相同):反证法得出,最后一个字符是Z(X和Y的LCS)的最后一个字符(假设位置为k),即Xm=Yn=Zk,不难看出,Zk-1∈LCS(Xm-1,Yn-1),即 Zk = Z(k-1) + 1,所以问题将转化为求Xm-1与Yn-1的LCS
②
Xm != Yn(最后一个字符不相同):
1. 当Xm != Zk时候,Zk∈LCS(Xm-1,Yn)
2. 当Yn != Zk时候,Zk∈LCS(Xm,Yn-1)
所以问题将转化为求 LCS(Xm-1,Yn)和 LCS(Xm,Yn-1)
由于上述当xm≠yn的情况中,求LCS(Xm-1 , Y)的长度与LCS(X , Yn-1)的长度,
这两个问题不是相互独立的:
两者都需要求LCS(Xm-1,Yn-1)的长度。
另外两个序列的LCS中包含了两个序列的前缀的LCS,故问题具有最优子结构性质考虑用动态规划法。
也就是LCS需要求三个方面的东西:
1. LCS(Xm-1,Yn-1) + 1
2. LCS(Xm-1 , Y) 和 LCS(X , Yn-1)
3. max{LCS(Xm-1 , Y) , LCS(X , Yn-1)}
转移方程:
c[i-1,j-1] + 1 ( Xm=Yn && i,j > 0 )
c[i,j] = max{ c[i-1,j] , c[i,j-1] } (Xm != Yn && i,j > 0)
0 ( i=0 || j=0 )
填写矩阵:
Procedure LCS_LENGTH(X,Y);
begin
m:=length[X];
n:=length[Y];
for i:=1 to m do c[i,0]:=0;
for j:=1 to n do c[0,j]:=0;
for i:=1 to m do
for j:=1 to n do
if x[i]=y[j] then
begin
c[i,j]:=c[i-1,j-1]+1;
b[i,j]:="↖";
end
else if c[i-1,j]≥c[i,j-1] then
begin
c[i,j]:=c[i-1,j];
b[i,j]:="↑";
end
else
begin
c[i,j]:=c[i,j-1];
b[i,j]:="←"
end;
return(c,b);
end;
#include<iostream>
using namespace std;
int lcs[100][100]; //记录LCS
char track[100][100]; //矩阵跟踪,用于最后输出 LCS串
char S[100], T[100];
void LCS(int slen, int tlen) {
int i, j;
//初始化矩阵
for (i = 0; i <= slen; i++) lcs[i][0] = 0;
for (i = 0; i <= tlen; i++) lcs[0][i] = 0;
for(i=1;i<=slen;i++)
for (j = 1; j <= tlen; j++) {
if (S[i] == T[j]) {
lcs[i][j] = lcs[i - 1][j - 1] + 1;
track[i][j] = 'Q'; // 左上
}
if (S[i] != T[j]) {
if (lcs[i - 1][j] >= lcs[i][j - 1]) {
lcs[i][j] = lcs[i - 1][j];
track[i][j] = 'W'; // 上
}
else {
lcs[i][j] = lcs[i][j - 1];
track[i][j] = 'A'; // 左
}
}
}
}
void printLcs(int slen,int tlen) {
int i, j;
char q[100]; //存储LCS串的栈
int top = 0; //栈顶
i = slen;
j = tlen;
cout << "length:" << lcs[i][j] << endl;
cout << "LCS:";
while (i != 0 && j != 0) {
if (track[i][j] == 'Q') {
q[++top] = T[j];
i--;
j--;
}
else if (track[i][j] == 'A') {
j--;
}
else {
i--;
}
}
while (top != 0)cout << q[top--]; // 根据矩阵踪到的LCS串是倒序的,需要反过来输出。
}
int main() {
int n, m, i;
cin >> n >> m;
for (i = 1; i <= n; i++)cin >> S[i];
for (i = 1; i <= m; i++)cin >> T[i];
LCS(n, m);
printLcs(n, m);
return 0;
}