最长公共子序列 过程图解
顾名思义,就是判断两个序列之间的最长的一样的子序列,这里需要注意最长公共子序列(longest commom sequence LCS)和最长公共子串(longest commom substring)是两个概念。
给定字符串中任意个连续的字符组成的子序列称为该串的子串,而子序列是将给定序列中零个或多个元素去掉之后的结果
求解LCS问题,可以使用动态规划来做,使用从c[i][j]
来记录序列
X
i
X_i
Xi和
Y
i
Y_i
Yi的最长公共子序列的长度。由最优字结构可建立递归关系如下
c [ i ] [ j ] = { 0 , i > 0 ; j = 0 c [ i − 1 ] [ j − 1 ] + 1 , i , j > 0 ; x i = y i m a x ( c [ i ] [ j − 1 ] , c [ i − 1 ] [ j ] ) , i , j > 0 ; x i ! = y i c[i][j]=\begin{cases} 0,{i>0;j=0} \\ {c[i-1][j-1]+1},{i,j>0;x_i=y_i} \\ {max({c[i][j-1],c[i-1][j]}}),{i,j>0;x_i!=y_i}\end{cases} c[i][j]=⎩⎪⎨⎪⎧0,i>0;j=0c[i−1][j−1]+1,i,j>0;xi=yimax(c[i][j−1],c[i−1][j]),i,j>0;xi!=yi
计算最优值算法伪代码
void LCSLength(int m ,int n, char *x,char *y,int **c,int **b){
int i,j;
for(i = 1; i < m; i++){
c[i][0] = 0;
}
for(i = 1; i < n; i++){
c[0][i] = 0;
}
for(i = 1; i <= m; i++){
for(j = 1; j <= n; j++){
if(x[i] == y[j]){
c[i][j] = c[i-1][j-1] + 1;
b[i][j] = 1;
}
else if(c[i-1][j] >= c[i][j-1]){
c[i][j] = c[i-1][j];
b[i][j] = 2;
}
else{
c[i][j] = c[i][j-1];
b[i][j] = 3;
}
}
}
}
构造最长公共子序列
由算法LCSLength计算得到数组b可用于快速构造最长公共子序列。首先从b[m][n]
开始,依其值在数组b中搜索。
- 当
b[i][j] = 1
时,表示 X i X_i Xi和 Y i Y_i Yi的最长公共子序列是由 X i − 1 X_{i-1} Xi−1和 Y j − 1 Y_{j-1} Yj−1的最长公共子序列在尾部加上 x i x_i xi所得到的子序列 - 当
b[i][j] = 2
时,表示 X i X_i Xi和 Y i Y_i Yi的最长公共子序列与 X i − 1 X_{i-1} Xi−1和 Y j Y_j Yj的最长公共子序列相同 - 当
b[i][j] = 3
时,表示 X i X_i Xi和 Y i Y_i Yi的最长公共子序列与 X i X_i Xi和 Y j − 1 Y_{j-1} Yj−1的最长公共子序列相同
伪代码如下
void LCS(int i,int j,char *x,char **b){
if(i == 0 || j == 0)
return;
if(b[i][j] == 1){
LCS(i-1,j-1,x,b);
cout<<x[i];
}
else if(b[i][j] == 2){
LCS(i-1,j,x,b);
}
else
LCS(i,j-1,x,b);
}
案例学习
求
X
=
A
B
A
C
D
A
B
C
B
A
X = ABACDABCBA
X=ABACDABCBA ,
Y
=
B
C
A
B
D
C
A
B
Y = BCABDCAB
Y=BCABDCAB的最长公共子序列