问题描述
一个给定序列的子序列是在该序列中删去若干元素后得到的序列。确切地说,若给定序列 X={x1,x2,⋯,xm} , 则另一序列 Z={z1,z2,⋯,zk} , 是X的子序列是指存在一个严格递增下标序列 {i1,i2,⋯,ik} , 使得对于所有的j = 1, 2, ⋯ , k有: zj=xij 。例如, 序列 Z=B,C,D,B 是序列 X={A,B,C,B,D,A,B} 的子序列, 相应的递增下标序列为 {2,3,5,7} 。
给定两个序列X和Y,当另一序列Z即是X的子序列又是Y的子序列时,称Z是序列X和Y的公共子序列。
最长公共子序列问题:给定两个序列 X={x1,x2,⋯,xm} 和 Y={y1,y2,⋯,yn} , 找出X和Y的最长公共子序列。
解题思路
设序列 X={x1,x2,⋯,xm} 和 Y={y1,y2,⋯,yn} 的最长公共子序列为 Z={z1,z2,⋯,zk} ,则
1. 若 xm=yn,则zk=xm=yn,且Zk−1是Xm−1和Yn−1 的最长公共子序列。
2. 若 xm≠yn , 且 zk≠xm , 则 Z是Xm−1和Y 的最长公共子序列。
3. 若 xm≠yn , 且 zk≠yn , 则 Z是X和Yn−1的最长公共子序列 。
其中, Xm−1={x1,x2,⋯,xm−1},Yn−1={y1,y2,⋯,ym−1},Zk−1={z1,z2,⋯,zk−1} ;
根据上面的关系建立一个递推关系。 用c[i][j]记录序列 Xi 和 Yj 的最长公共子序列的长度。其中, Xi={x1,x2,⋯,xi} , Yj={y1,y2,⋯,yj} 。可建立如下的递归关系:
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≠yj
则对于序列
X和Y 的最长公共子序列长度为:
ans=c[m][n]
代码
#include <stdio.h>
#include <stdlib.h>
#define maxn 100
int c[maxn][maxn]; //保存最长公共子序列的长度
int b[maxn][maxn]; //记录c[i][j]的值是由哪一个子问题的解得到的
void LCS(char x[], char y[], int m, int n);
void PrintLCS(char x[], int i, int j);
int main()
{
char x[7] = "ABCBDAB";
char y[6] = "BDCABA";
printf("最长公共子序列为:");
LCS(x, y, 7, 6);
printf("\n\n\n");
int i, j;
for (i = 0; i <= 7; i++)
{//打印二维表
for (j = 0; j <= 6; j++)
printf("%3d", c[i][j]);
printf("\n");
}
printf("\n");
return 0;
}
void LCS(char x[], char y[], int m, int n)
{
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-1] == y[j-1])
{
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;
}
}
}
PrintLCS(x, m, n); //打印最长公共子序列
}
void PrintLCS(char x[], int i, int j)
{
if (i == -1 || j == -1)
return;
if (b[i][j] == 1)
{
PrintLCS(x, i - 1, j - 1);
printf("%c", x[i-1]);
}
else if (b[i][j] == 2)
PrintLCS(x, i - 1, j);
else
PrintLCS(x, i, j - 1);
}