今天看了七月在线算法课。再一次认识了LCS,现在整理记录:

LCS(Longest Common Subsequence)最长公共子序列。

一个序列S任意删除若干个字符得到新序列T,那么T叫做S的子序列。

两个序列X和Y的公共子序列中,长度最长的那个叫X和Y的最长公共子序列。

例如:

字符串13455和245576的最长公共子序列为455.

字符串acdfg和adfc的最长公共子序列为adf.

注意:这里要区别最长公共子串LCS(Longest Common Substring),这个要求的是字符串必须连续。

这里偷懒,直接拿了老师的PPT。记好下面的记号。

下面就进行LCS解法的探索:

当x=y的时候:

然后我们就能得到:

此处的意思就是:当两个子序列中结尾字符是相等的,那么最长的子序列就肯定是这个相等值+前面的的最长长度了,这样他们的长度才能是最长的。

例子:

那么现在我们再考虑X不等于Y的情况:

此处的解释:

当我们认为是我们子序列的子串的最后一个元素不相等,那么他的最长子序列一定来自两个地方,假设去掉X序列的最后一个元素,那么最长子序列一定产生于图中A+B+Yn,同理一样, B+A+Xn.

例子:

总结:

怎么求呢?C[m,n]是一个正整数的值,表示序列m和n之间的最大的长度。

当两个字符串有一个是0 ,那么LCS一定是0;

例子:

右下角的数据怎么计算呢?

经过上面的分析,我们知道两个序列有一个是0 的那么LCS都是0 ;所以第0行第0列都是0.

图中红圈内1怎么得来的呢?

红圈内的x是B,y是D,即x不等于y.那么我们就要看红圈上左边和上边 的数值了,选择一个大的数值写入。

那么以此类推,当x=y 的时候,我们直接选择红圈左上的值+1,写入即可。

两个例子来自点击打开链接

代码一是让你输入两个序列,然后输出最长公共子序列和长度。

#include

#include

#include

int LCSLength(char* str1, char* str2, int **b)

{

int i,j,length1,length2,len;

length1 = strlen(str1);

length2 = strlen(str2);

//双指针的方法申请动态二维数组

int **c = new int*[length1+1]; //共有length1+1行

for(i = 0; i < length1+1; i++)

c[i] = new int[length2+1];//共有length2+1列

for(i = 0; i < length1+1; i++)

c[i][0]=0; //第0列都初始化为0

for(j = 0; j < length2+1; j++)

c[0][j]=0; //第0行都初始化为0

for(i = 1; i < length1+1; i++)

{

for(j = 1; j < length2+1; j++)

{

if(str1[i-1]==str2[j-1])//由于c[][]的0行0列没有使用,c[][]的第i行元素对应str1的第i-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;

}

}

}

/*

for(i= 0; i < length1+1; i++)

{

for(j = 0; j < length2+1; j++)

printf("%d ",c[i][j]);

printf("\n");

}

*/

len=c[length1][length2];

for(i = 0; i < length1+1; i++) //释放动态申请的二维数组

delete[] c[i];

delete[] c;

return len;

}

void PrintLCS(int **b, char *str1, int i, int j)

{

if(i==0 || j==0)

return ;

if(b[i][j]==0)

{

PrintLCS(b, str1, i-1, j-1);//从后面开始递归,所以要先递归到子串的前面,然后从前往后开始输出子串

printf("%c",str1[i-1]);//c[][]的第i行元素对应str1的第i-1个元素

}

else if(b[i][j]==1)

PrintLCS(b, str1, i-1, j);

else

PrintLCS(b, str1, i, j-1);

}

int main(void)

{

char str1[100],str2[100];

int i,length1,length2,len;

printf("请输入第一个字符串:");

gets(str1);

printf("请输入第二个字符串:");

gets(str2);

length1 = strlen(str1);

length2 = strlen(str2);

//双指针的方法申请动态二维数组

int **b = new int*[length1+1];

for(i= 0; i < length1+1; i++)

b[i] = new int[length2+1];

len=LCSLength(str1,str2,b);

printf("最长公共子序列的长度为:%d\n",len);

printf("最长公共子序列为:");

PrintLCS(b,str1,length1,length2);

printf("\n");

for(i = 0; i < length1+1; i++)//释放动态申请的二维数组

delete[] b[i];

delete[] b;

system("pause");

return 0;

}