题目
最长公共子序列问题是在序列X和Y的公共子序列中查找长度最长的公共子序列,而最长公共子序列往往不止一个。
例如:
X
=
(
A
,
B
,
C
,
B
,
D
,
A
,
B
)
,
Y
=
(
B
,
D
,
C
,
A
,
B
,
A
)
,
则
Z
=
(
B
,
C
,
B
,
A
)
,
Z
1
=
(
B
,
C
,
A
,
B
)
,
Z
2
=
(
B
,
D
,
A
,
B
)
,
均
属
于
L
C
S
(
X
,
Y
)
,
即
X
,
Y
的
最
长
公
共
子
序
列
有
3
个
。
X= (A,B,C,B,D,A,B),Y = (B,D,C,A,B,A),则Z = (B,C,B,A),Z_1 = (B,C,A,B),Z_2 = (B,D,A,B),均属于LCS(X,Y),即X,Y的最长公共子序列有3个。
X=(A,B,C,B,D,A,B),Y=(B,D,C,A,B,A),则Z=(B,C,B,A),Z1=(B,C,A,B),Z2=(B,D,A,B),均属于LCS(X,Y),即X,Y的最长公共子序列有3个。
定理: LCS的最优子结构性质
设序列:
X
=
(
x
1
,
x
2
,
.
.
.
.
,
x
n
)
,
Y
=
(
y
1
,
y
2
,
.
.
.
,
y
n
)
的
一
个
最
长
公
共
子
序
列
Z
=
z
1
,
z
2
,
.
.
.
,
z
n
X= (x_1,x_2,....,x_n),Y = (y_1,y_2,...,y_n)的一个最长公共子序列Z={z_1,z_2,...,z_n}
X=(x1,x2,....,xn),Y=(y1,y2,...,yn)的一个最长公共子序列Z=z1,z2,...,zn,则:
(1)若
x
m
=
y
n
,
则
z
k
=
x
m
=
y
n
,
且
Z
k
−
1
是
X
m
−
1
和
Y
n
−
1
x_m = y_n,则z_k=x_m=y_n,且Z_{k-1}是X_{m-1}和Y_{n-1}
xm=yn,则zk=xm=yn,且Zk−1是Xm−1和Yn−1的最长公共子序列;
(2)若
x
m
=
̸
y
n
,
则
z
k
=
̸
x
m
,
则
Z
是
X
m
−
1
和
Y
x_m =\not y_n,则z_k =\not x_m,则Z是X_{m-1}和Y
xm=yn,则zk=xm,则Z是Xm−1和Y的最长公共子序列;
(3)若
x
m
=
̸
y
n
,
则
z
k
=
̸
y
n
,
且
Z
是
X
和
Y
n
−
1
x_m =\not y_n,则z_k=\not y_n,且Z是X和Y_{n-1}
xm=yn,则zk=yn,且Z是X和Yn−1的最长公共子序列。
建立递归关系
我们定义c[i,j]记录序列
X
i
X_i
Xi和
Y
j
Y_j
Yj的最长公共子序列的长度。
状态转移方程为:
c [ i ] [ j ] = { 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 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 =\not y_i \end{cases} c[i][j]=⎩⎪⎨⎪⎧0c[i−1][j−1]+1max {c[i][j−1],c[i−1][j]}i=0,j=0i,j>0;xi=yii,j>0;xi=yi
代码
#include<stdio.h>
#define NUM 100
int c[NUM][NUM];//记录序列Xi和Yj的最长公共子序列的长度
int b[NUM][NUM];//用来记录比较情况
void LCSLength(int m, int n, const char x[],char y[])//计算最优值
{
int i,j;
for (i = 1; i <= m; i++) c[i][0] = 0;//第0列全为0(此时Y序列长度为0)
for (i = 1; i <= n; i++) c[0][i] = 0;//第0行全为0(此时X序列长度为0)
for (i = 1; i <= m; i++)
for (j = 1; j <= n; j++)
{
if (x[i]==y[j]) //①i,j>0且Xi与Yj相等
{
c[i][j]=c[i-1][j-1]+1;
b[i][j]=1;
}else if (c[i-1][j]>=c[i][j-1]) {//②max c[i-1][j],i,j>0,Xi不等于Yj
c[i][j]=c[i-1][j];
b[i][j]=2;
}else { //③max c[i][j-1],i,j>0,Xi不等于Yj
c[i][j]=c[i][j-1];
b[i][j]=3;
}
}
}
void LCS(int i,int j,char x[])// 构造最优解结构(递归)
{
if (i ==0 || j==0) return;
if (b[i][j]== 1){ LCS(i-1,j-1,x); printf("%c",x[i]); }
else if (b[i][j]== 2) LCS(i-1,j,x);
else LCS(i,j-1,x);
}
int main()
{
char x[NUM];
char y[NUM];
int m, n;
scanf("%d\n", &m);
for (int i=1; i<=m; i++)//读取X序列
scanf("%c", &x[i]);
scanf("%d\n", &n);
for (int i=1; i<=n; i++)//读取Y序列
scanf("%c", &y[i]);
LCSLength(m, n, x, y);
printf("%d\n", c[m][n]);//输出最优值
LCS(m,n,x);//输出最优解结构(此时x和y都无所谓)
return 0;
}
输入
7
ABCBDAB
6
BDCABA
输出
4
BCBA
总结
怎么讲呢,学到了很多Markdown公式的写法,而矩阵连乘积正是动态规划的一个经典例子。学好动态是算法的一个深化吧!