【LuoguP2516】 [HAOI2010]最长公共子序列

题目链接

题意

求出两个串的最长公共子序列长度以及个数

Sol

有点难像

主要是方案不怎么会算

分类讨论,要把情况扯清楚

F[i][j] F [ i ] [ j ] 表示考虑到了 i,j i , j 的长可能的长度, f[i][j] f [ i ] [ j ] 表示最长长度下的方案数

这里我们的数组记录的不是严格的,相当于是个前缀和,所以分类讨论很重要

既然相当于是个二维前缀和,那么自然对于 (i,j) ( i , j ) ,就考虑从 (i1,j),(i,j1),(i1,j1) ( i − 1 , j ) , ( i , j − 1 ) , ( i − 1 , j − 1 ) 转移

假设我们考虑到了 (i,j) ( i , j )

case1:
s[i]==[j] s [ i ] == [ j ]
这个比较显然,直接:

F[i][j]=F[i1][j1]+1f[i][j]+=f[i1][j1] F [ i ] [ j ] = F [ i − 1 ] [ j − 1 ] + 1 f [ i ] [ j ] + = f [ i − 1 ] [ j − 1 ]

case2:
s[i]!=t[j] s [ i ] ! = t [ j ]
那么可以写一步是从 (i1,j),(i,j1) ( i − 1 , j ) , ( i , j − 1 ) 转移过来,但是是多余的,因为我们可以直接写在讨论长度关系的时候

转移完了我们要做个类似二维前缀和的东西
首先看下把 (i1,j) ( i − 1 , j ) 的前缀考虑进来
由于要最长,那么先看长度关系,如果小于,那么显然的是直接把 (i1,j) ( i − 1 , j ) 赋值给 (i,j) ( i , j ) 因为要最优
如果相等,那么方案数加起来

然后是 (i,j1) ( i , j − 1 ) ,这里是类似(其实是一样)的

既然这里是个二维前缀和,我们显然似乎会算重一些东西,我们可能把 (i1,j1) ( i − 1 , j − 1 ) 中的一些方案算重了
注意到显然当前 (i,j) ( i , j ) 的最优解长度一定不会比 (i1,j1) ( i − 1 , j − 1 ) 差,那么我们算重的就是 (i1,j) ( i − 1 , j ) (i,j1) ( i , j − 1 ) 中重复的部分
反映在实际情况就是有没有 (i,j) ( i , j ) 对答案无影响,即当前的全是由后面的转移过来的
那么这时算多了 (i1,j1) ( i − 1 , j − 1 ) ,减掉就行了

代码:

#include<bits/stdc++.h>
#define Set(a,b) memset(a,b,sizeof(a))
using namespace std;
const int N=5001;
const int mod=1e8;
char a[N];
char c[N];
int F[2][N],f[2][N];
inline void upd(int &X,int Y){X+=Y;if(X>=mod) X-=mod;}
int main()
{
    int n,m;
    scanf("%s",a+1);
    scanf("%s",c+1);
    n=strlen(a+1);m=strlen(c+1);--n,--m;
    int cnt=0;
    for(register int i=0;i<=n;++i) f[cnt][i]=1;
    for(register int i=1;i<=n;++i){
        cnt^=1;
        Set(F[cnt],0);Set(f[cnt],0);
        f[cnt][0]=1;
        for(register int j=1;j<=m;++j){
            if(a[i]==c[j]) F[cnt][j]=F[cnt^1][j-1]+1,f[cnt][j]=f[cnt^1][j-1];
            if(F[cnt][j]<F[cnt^1][j]) F[cnt][j]=F[cnt^1][j],f[cnt][j]=f[cnt^1][j];
            else if(F[cnt][j]==F[cnt^1][j]) upd(f[cnt][j],f[cnt^1][j]);

            if(F[cnt][j]==F[cnt][j-1]) upd(f[cnt][j],f[cnt][j-1]);//前缀和
            else if(F[cnt][j]<F[cnt][j-1]) F[cnt][j]=F[cnt][j-1],f[cnt][j]=f[cnt][j-1];//可能与该位匹配不优

            if(F[cnt][j]==F[cnt^1][j]&&F[cnt][j]==F[cnt][j-1]&&F[cnt][j]==F[cnt^1][j-1]) {
                f[cnt][j]-=f[cnt^1][j-1];//新加的答案和当前这两位没有关系,那么对于上面和下面的考虑 算了两次重复的方案
                if(f[cnt][j]<0) f[cnt][j]+=mod;
            }
        }
    }
    printf("%d\n%d\n",F[cnt][m],f[cnt][m]);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值