题目的大意是已知两个字符串,求两个字符串的最大公共子序列。
假设串X[i]是大串,Y[j]是小串,(i>j)那么在问题的分析中,有这样的假设:
设C[][],来表示最大子串的长度,Z[]表示最大子串,如:C[i][j]表示X[i]与Y[j]的最大子串长度,那么有:
①如果X[i]=Y[j],那么,C[i][j]=C[i-1][j-1]+1,表示X[i-1],Y[j-1]的最大字串长度,Z[i]=X[i]=Y[j]。
②如果X[i]≠Y[j],且X[i]≠Z[i],则可能Y[j]=Z[i]=X[i-n](n>0),由于递推算法的运算,n最小取1,那么最后一个子序数Z[i]是前X[i-1]与前Y[j]的公共子序列,那么,有C[i][j]=C[i-1][j],由于运算是从后往前的递推运算,所以在i--,j--的运算过程中,会对Y[j],X[i-1]进行判断。
③如果X[i]≠Y[j]且Y[j]≠Z[i],则可能X[i]=Z[i]=Y[j-n](n>0),由于递推算法的运算,n最小取1,那么最后一个子序数Z[i]是前Y[j-1]与前X[i]的公共子序列,那么,有C[i][j]=C[i][j-1],由于运算是从后往前的递推运算,所以在i--,j--的运算过程中,会对Y[j-1],X[i]进行判断。
但是存在的一个问题是,我们并不知道Z的具体数目,则可以另加判断。
①当X[i]==Y[j]时可以直接进行运算:C[i][j]=C[i-1][j-1]+1。
②当两者不相等时,判断是否有X[i]与Y或者X与Y[j]构成新的子序列元素,,即判断C[i-1][j]与C[i][j-1]的大小关系。C[i][j]取极大,表示当前的最大子序列元素个数。
最后C[i][j]即为解。
如果想要得到子序列的具体输出情况,为了避免重复输出,可以另加数组Z[i][j]来存放参数,在对C数组赋值时同时也进行赋值。
如果是情况①,那么可以赋值Z[i][j]=1,输出的时候只要是1就输出X[i]或者Y[j],同时传参的项数为i-1,j-1。
如果是情况②,那么可以赋值为2,不进行输出,递归参数即为i-1,j。
如果是情况③,那么可以赋值为3,不进行输出,递归参数即为i,j-1。
具体代码如下:
#include <stdio.h>
#include <string.h>
#define MAXLEN 100
void LCSLength(char *x, char *y, int m, int n, int c[][MAXLEN], int b[][MAXLEN])
{
int i, j;
for(i = 0; i <= m; i++)
c[i][0] = 0;
for(j = 1; j <= n; j++)
c[0][j] = 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] = 0;
}
else if(c[i-1][j] >= c[i][j-1])
{
c[i][j] = c[i-1][j];
b[i][j] = 1;
}
else
{
c[i][j] = c[i][j-1];
b[i][j] = -1;
}
}
}
}
void PrintLCS(int b[][MAXLEN], char *x, int i, int j)
{
if(i == 0 || j == 0)
return;
if(b[i][j] == 0)
{
PrintLCS(b, x, i-1, j-1);
printf("%c ", x[i-1]);
}
else if(b[i][j] == 1)
PrintLCS(b, x, i-1, j);
else
PrintLCS(b, x, i, j-1);
}
int main(int argc, char **argv)
{
char x[MAXLEN] = {"ABCBDAB"};
char y[MAXLEN] = {"BDCABA"};
int b[MAXLEN][MAXLEN];
int c[MAXLEN][MAXLEN];
int m, n;
m = strlen(x);
n = strlen(y);
LCSLength(x, y, m, n, c, b);
PrintLCS(b, x, m, n);
return 0;
}