洛谷P2679 [NOIP2015]子串

动态规划 专栏收录该内容
75 篇文章 0 订阅

毒瘤dp

思维量巨大,于是我抄了题解

我们先写一个假的dp转移方程:f[i][j][k]表示a串到第i位,b串到第j位,一共用了a中的k个子串的方案数, 咋转移?考虑当前考虑的a的这位是单独成一个串还是与前面的连在一起于是乎就有了f[i][j][k]=f[i-1][j-1][k]+f[i-1][j-1][k-1],前提条件是a[i]=b[j],但如果不相等呢?那这个方程就出锅了,所以我们新考虑了一个东西:当前位选还是不选,显然,对于这两种情况无法在同一个dp数组内表达出来,所以我们定义

g[i][j][k]表示a到第i位,b到第j位,已经用了k个子串,且必须要选上a[i]的方案数

f[i][j][k]表示a到第i位,b到第j位,已经用了k个子串,当前a[i]可以选也可以不选的方案数

然后我们看一下如何转移

对于g[i][j][k],a[i]必须选上,那么它可以和前面的连在一起,也可以单独成为一个串,单独成为一个串的情况,就无需考虑前一位是否选了,所以是f[i-1][j-1][k-1],若与前面的连在一起,那么前一位必须选,就是g[i-1][j-1][k],所以,g[i][j][k]=f[i-1][j-1][k-1]+g[i-1][j-1][k]

对于f[i][j][k],a[i]可选可不选,对于选它,那么对应的就是g[i][j][k]的方案数,对于不选它,那么就是f[i-1][j][k],所以得到f[i][j][k]=f[i-1][j][k]+g[i][j][k]

然后我们就dp完了

还有一个严肃的问题就是数组开不下,那就滚动一维

代码

//By AcerMo
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int mod=1e9+7;
int n,m,e;
char a[1050],b[1050];
int f[1050][1050][3],g[1050][1050][3];
signed main()
{
    scanf("%d%d%d",&n,&m,&e);
    scanf("%s",a+1);scanf("%s",b+1);
    int x=1,y=0;f[0][0][0]=1;
    for (int i=1;i<=n;swap(x,y),i++)
    {
        f[0][0][x]=1;
        for (int k=1;k<=m;k++)
        for (int j=1;j<=e;j++)
        {
            if (a[i]==b[k]) g[k][j][x]=(g[k-1][j][y]+f[k-1][j-1][y])%mod;
            else g[k][j][x]=0;
            f[k][j][x]=(f[k][j][y]+g[k][j][x])%mod;
        }
    }
    cout<<f[m][e][y];
    return 0;
}
  • 0
    点赞
  • 1
    评论
  • 1
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

打赏
文章很值,打赏犒劳作者一下
相关推荐
©️2020 CSDN 皮肤主题: 技术黑板 设计师:CSDN官方博客 返回首页

打赏

AcerMo

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值