您的位置:首页 > 其它

大白话讲解LCS(最长公共子序列)

2018-04-03 16:53 405 查看
今天看了七月在线算法课。再一次认识了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 <stdio.h>
#include <string.h>
#include <stdlib.h>
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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  LCS