最长公共子序列(LCS)问题
我们经常遇到:给定两个序列,求这两个序列的最长公共子串问题,形式化表示:给定两个序列X=<>和Y=<>,求X和Y长度最长的公共子序列,本文将利用动态规划方法高效地求解LCS(连续和不连续),并分别给出在C编写的程序。
1、动态规划法
通过组合子问题的解来求解原问题,通常按照如下的4个步骤来设计一个动态规划算法:
<1>、刻画一个最优解的结构特征。
<2>、递归的定义最优解的值。
<3>、计算最优解的值,通常采用自底向上的方法。
<4>、利用计算出的信息构造一个最优解。
2、最长公共子序列(不连续)
【LCS的最优子结构】令X=<>和Y=<>为两个序列,为X和Y的任意LCS。
<1>、如果,则且是和的一个LCS。
<2>、如果,则且是和的一个LCS。
<3>、如果,则且是和的一个LCS。
可以通过定理方法严谨证明!
根据LCS问题的最优子结构的性质,可以得到如下公式:
C程序如下:
int lcs_length(char str1[],charstr2[])
{
intm=strlen(str1);
intn=strlen(str2);
int**c;
c=(int**)malloc(sizeof(int*)*(m+1));
for(intk=0;k<=m;k++)
{
c[k]=(int*)malloc(sizeof(int)*(n+1));
}
inti,j;
for(i=0;i<=m;i++)
{
for(j=0;j<=n;j++)
{
c[i][j]=0;
}
}
for(i=1;i<=m;i++)
{
for(j=1;j<=n;j++)
{
if(str1[i]==str2[j])
c[i][j]=c[i-1][j-1]+1;
elseif(c[i-1][j]>=c[i][j-1])
c[i][j]=c[i-1][j];
else
c[i][j]=c[i][j-1];
}
}
intmax=0;
for(i=1;i<=m;i++)
{
for(j=1;j<=n;j++)
{
if(c[i][j]>max)
max=c[i][j];
}
}
free(c);
returnmax;
}
3、最长公共连续子序列(连续)
下面分析一下最长公共连续子序列的求法:因为要求的是公共串必须为连续的,因而在出现的情况时,此时对于就需要从零重新开始。
即对应公式为:
C程序如下:
int strict_lcs_length(charstr1[],char str2[])
{
intm=strlen(str1);
intn=strlen(str2);
int**c;
c=(int**)malloc(sizeof(int*)*(m+1));
for(intk=0;k<=m;k++)
{
c[k]=(int*)malloc(sizeof(int)*(n+1));
}
inti,j;
for(i=0;i<=m;i++)
{
for(j=0;j<=n;j++)
{
c[i][j]=0;
}
}
for(i=1;i<=m;i++)
{
for(j=1;j<=n;j++)
{
if(str1[i]==str2[j])
c[i][j]=c[i-1][j-1]+1;
elsec[i][j]=0;
}
}
intmax=0;
for(i=1;i<=m;i++)
{
for(j=1;j<=n;j++)
{
if(c[i][j]>max)
max=c[i][j];
}
}
free(c);
returnmax;
}
上述算法的时间复杂度为,空间复杂度为,上述算法还可以进一步改进,我们可以讲查找最大长度和对应字符的工作放在狗仔举证的过程中完成,这样可以节省矩阵最大元素查找的时间。
同样,对于空间复杂度也可做以改进,将算法的空间复杂度降为,在上述构造中,当矩阵的第行的值计算完成后,第行的值就没有作用了,即使最大的长度出现在第行,我们也已经记录了。故而我们可以用一维向量来处理,向量的当前值对应第i行,向量的下一行对应第i+1行,其中注意内存循环要从最大值朝最小值变化,否则同一行下表小的更新,会覆盖掉上一行数值,导致更新错误。
其所对应的C程序代码为:
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<malloc.h>
//计算最长连续公共子串
int lcs_length(char str1[],charstr2[])
{
if(NULL== str1 || NULL == str2) //如果字符串为空,返回-1
{
return-1;
}
intm=strlen(str1);//计算str1的长度
intn=strlen(str2);//计算str2的长度
int*c=(int*)malloc(sizeof(int)*(n+1));
inti,j;
for(i=0;i<n;i++)
{
c[i]= 0;
}
intmax_len = 0;
for(i=0;i<m;i++)
{
for(j=n;j>0;j--)//注意,必须j从大到小变化,否则还需要使用的旧值被覆盖
{
if(str1[i]==str2[j-1])
{
c[j]=c[j-1]+1;
if(max_len<c[j])//记录最大值
{
max_len=c[j];
}
}
else
c[j]=0;
}
}
free(c);
returnmax_len;//返回最大值
}