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

1:基本概念

子序列(Subsequence):在不改变原字符串元素的先后位置
公共子序列:两个字符串共有的子序列
再介绍一个相似的概念
子串(Substring):元素连续的子序列

下面举个例子:
现在有两个字符串
A:abcdefg
B:appleorange

abc:A的子序列,A的子串
ade:A的子序列
sad:什么都不是
apple:B的子序列,B的子串
ae:A和B的公共子序列

2:功能实现

2.1:暴力查找法 O(2^(n+m))

……

……

……

……

……

……

长度为n的字符串就有2的n次方个子序列,暴力查找不现实,这里也不贴代码了。

2.2:动态规划法 O(n*m)

特征分析:
动态规划的特点就是当前状态可以通过之前的状态确定,且可以得出状态转移方程。
所以解决动态规划问题最重要的步骤就是分析特征。
先丢出转移方程
dp[i,j]表示匹配到a[i-1]和b[j-1]时最长公共子序列长度
(为了书写简便,dp[i,j]一般对应着a[i-1],b[i-1])

           {0 若i==0或j==0
dp[i,j]={dp[i-1,j-1]+1 若i,j>0且a[i-1]==b[j-1]
           {max(dp[i-1,j],dp[i,j-1]     若i,j>0且a[i-1]!=b[j-1]

再来分析
如果 i==0或j==0 的话  只有一个字符串,则最长公共子序列长度为0 dp[i,j]=0
如果 i,j>0且a[i]=b[j] 当前字符相同,最长公共子序列长度应+1,dp[i,j]=dp[i-1,j-1]+1
如果 i,j>0且a[i]!=b[j]当前字符不同,最长公共子序列长度不变,dp[i,j]=max(dp[i-1,j],dp[i][j-1])

来个例子,首先我们要有两个字符串,duang!
A:abcdefabcde
B:azddbcabezz
abcdefabcde
azddbcabezz

然后上个图!

已初始化0


然后填充第一行


再处理第二行

一口气完成所有行

加个颜色区分一下


是不是一下就很直观了呢

贴个代码

void LCS_1(char *c,char *d){
    int dp[MAXX][MAXX];
    int len1,len2,i,j,k;
    len1=strlen(c);
    len2=strlen(d);
    for(i=0;i<=len1;i++){
        for(j=0;j<=len2;j++){
            if(i==0||j==0)  dp[i][j]=0;
            else if(c[i-1]==d[j-1])dp[i][j]=dp[i-1][j-1]+1;
            else if(c[i-1]!=d[j-1])dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
        }
    }
    printf("%d\n",dp[len1][len2]);
    LCS_SUB(c,d,dp);
}
void LCS_2(char *c,char *d){//滚动数组法
    int dp[2][MAXX];
    int len1,len2,i,j,k;
    len1=strlen(c);
    len2=strlen(d);
    for(i=0;i<=len1;i++){
        for(j=0;j<=len2;j++){
            if(i==0||j==0)  dp[1][j]=0;
            else if(c[i-1]==d[j-1])dp[1][j]=dp[0][j-1]+1;
            else if(c[i-1]!=d[j-1])dp[1][j]=max(dp[0][j],dp[1][j-1]);
        }
        for(j=0;j<len2;j++){
            dp[0][j]=dp[1][j];
        }
    }
    printf("%d\n",dp[1][len2]);
}

3:输出公共子序列

首先对上面的图进行一下加工

我们可以清楚的看到一条回溯路径,
这就是我们要的子序列了。
然后从右下往左上查找,再逆序输出即可

void LCS_SUB(char *c,char *d,int dp[][MAXX]){
    int i,j,k;
    i=strlen(c);
    j=strlen(d);
    char e[MAXX];
    int len=0;
    while(i!=0&&j!=0){
        if(dp[i-1][j]==dp[i][j-1]&&dp[i-1][j]==dp[i-1][j-1]&&dp[i-1][j]<dp[i][j]){
            e[len]=c[i-1];
            len++;
            i--;
            j--;
        }
        else if(dp[i][j-1]==dp[i][j]){
            j--;
        }
        else if(dp[i-1][j]==dp[i][j]){
            i--;
        }
    }
    for(i=len-1;i>=0;i--){
        printf("%c",e[i]);
    }
    printf("\n");
}



  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值