P2516 [HAOI2010]最长公共子序列 题解(LCS)

题目链接

最长公共子序列

解题思路

第一思路:

1.用\(length[i][j]\)表示\(a\)串的前\(i\)个字符与\(b\)串的前\(j\)个字符重叠的最长子串长度
2.用\(num[i][j]\)表示 \(a\)串的前\(i\)个字符与\(b\)串的前\(j\)个字符重叠的最长子串个数
则求\(length[i][j],num[i][j]\)时有以下递推关系:
\(length[i][j]:\)
如果当前两串结尾字符相等,则\(length[i][j]=length[i-1][j-1]+1\)
否则\(length[i][j]=max(length[i-1][j],length[i][j-1])\)
\(num[i][j]:\)
如果\(length[i][j]\)\(length[i-1][j]\)相等,\(num[i][j]\)可加\(num[i-1][j]\)
如果\(length[i][j]\)\(length[i][j-1]\)相等,\(num[i][j]\)可加\(num[i][j-1]\)
如果\(length[i][j]\)\(length[i-1][j-1]\)相等,则\(num[i][j]\)多加了个\(num[i-1][j-1]\),需要减去
代码:

#include<stdio.h>
#include<string.h>
#define max(a,b) (a>b?a:b)
int length[5010][5010],num[5010][5010],w=100000000;
char a[5010],b[5010];
int main(){
    int i,j,la,lb;
    scanf("%s%s",a,b);
    la=strlen(a)-1;lb=strlen(b)-1;
    for(i=0;i<=la;i++)num[i][0]=1;
    for(i=0;i<=lb;i++)num[0][i]=1;
    for(i=1;i<=la;i++){
        for(j=1;j<=lb;j++){
            if(a[i-1]==b[j-1]){
                length[i][j]=length[i-1][j-1]+1;
                num[i][j]=num[i-1][j-1];
            }
            else{
                length[i][j]=max(length[i-1][j],length[i][j-1]);
                if(length[i][j]==length[i-1][j-1])num[i][j]-=num[i-1][j-1];
                num[i][j]+=w;
            }
            if(length[i-1][j]==length[i][j])num[i][j]+=num[i-1][j];
            if(length[i][j-1]==length[i][j])num[i][j]+=num[i][j-1];
            num[i][j]%=w;
            length[i][j]%=w;
        }
    }
    printf("%d\n%d",length[la][lb],num[la][lb]);
    return 0;
}

提交效果
1520916-20181120152516618-309861399.png

优化代码

考虑优化代码。
考虑到\(length\)\(num\)数组当前状态都只与上一状态相关,可以用滚动数组优化空间和时间。

AC代码

#include<stdio.h>
#include<string.h>
#define max(a,b) (a>b?a:b)
int length[2][5010],num[2][5010],w=100000000;
char a[5010],b[5010];
int main(){
    int i,j,la,lb;
    scanf("%s%s",a,b);
    la=strlen(a)-1;lb=strlen(b)-1;
    for(i=0;i<=lb;i++)num[0][i]=1;
    num[1][0]=1;
    for(i=1;i<=la;i++){
        int temp=i%2;
        for(j=1;j<=lb;j++){
            num[temp][j]=0;//滚动数组一定要注意这一点 
            if(a[i-1]==b[j-1]){
                length[temp][j]=length[temp^1][j-1]+1;
                num[temp][j]=num[temp^1][j-1];
            }
            else{
                length[temp][j]=max(length[temp^1][j],length[temp][j-1]);
                if(length[temp][j]==length[temp^1][j-1])num[temp][j]-=num[temp^1][j-1];
                num[temp][j]+=w;
            }
            if(length[temp^1][j]==length[temp][j])num[temp][j]+=num[temp^1][j];
            if(length[temp][j-1]==length[temp][j])num[temp][j]+=num[temp][j-1];
            num[temp][j]%=w;
            length[temp][j]%=w;
        }
    }
    printf("%d\n%d",length[la%2][lb],num[la%2][lb]);
    return 0;
}

提交效果
1520916-20181120152605281-933445134.png

转载于:https://www.cnblogs.com/Potassium/p/9989173.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值