【NOIP2015】子串

传送门
在赛场上这道题坑了我不少的时间……还是我太菜了,不过还好写出来了(可我还是被卡常了……90分,我优化了一下常数就过了……不开心),时间复杂度O(nmk)
DP的思路很简单
f(k,i,j)表示分了k段,用了第一个串中的前i个数字,已经构成了第二个串的前j个的方案数

f(k,i,j)=f(k1,l,j1)f(k1,l,j1)+f(k,i1,j1)s1[i]==s2[j]s1[i1]!=s2[j1]s1[i]==s2[j]s1[i1]==s2[j1]0<l<i0<l<i

第一个转移是独立开辟出一个部分的可能数,第二个是算上之前就已经分了k个部分后把第k个部分扩大的方案数。还是比较好理解的,当然f数组要滚动第一维,不然会爆内存。
我觉得这一道题的难度,都和当年乌龟棋在当时的难度差不多了。但这道题的70分算法 O(nm2k)比较好写,所以拉不卡差距。
相信大家在看了我的状态转移之后能想出 O(nmk)的方法。其实我们需要维护的就只是那一个 用一个tmp数组保存一下就好了(详见代码)
代码
#include<cstdio>
#include<cstring>
typedef unsigned int LL;
const LL MOD = 1000000007LL;
const int MAXN = 1005;
int n, m, K;
LL f[2][MAXN][205];
LL tmp[2][MAXN][205];
char s1[MAXN], s2[MAXN];
LL ans = 0;
int main()
{
    scanf("%d%d%d", &n, &m, &K);
    scanf("%s%s", s1+1, s2+1);
    f[0][0][0] = 1;
    tmp[0][0][0] = 1;
    for(int i = 0; i <= n; i ++)
        tmp[0][i][0] = 1;
    for(int k = 1; k <= K; k ++) {
        memset(tmp[k&1], 0, sizeof (tmp[k&1]));
        memset(f[k&1], 0, sizeof (f[k&1]));
        for(int i = 1; i <= n; i ++) {
            for(int j = 1; j <= m; j ++) {
                if(s1[i] == s2[j]) {
                    f[k&1][i][j] = tmp[(k+1)&1][i-1][j-1];
                    if(s1[i-1] == s2[j-1]) f[k&1][i][j] = (f[k&1][i][j] + f[k&1][i-1][j-1]) % MOD;
                    if(f[k&1][i][j] >= MOD) f[k&1][i][j] -= MOD;
                }
                tmp[k&1][i][j] = (tmp[k&1][i][j] + f[k&1][i][j]) % MOD + tmp[k&1][i-1][j];
                if(tmp[k&1][i][j] > MOD) tmp[k&1][i][j] -= MOD;
            }
        }
    }
    for(int i = 1; i <= n; i ++)
        ans = (ans + f[K&1][i][m]) % MOD;
    printf("%d\n", ans);
    return 0;
}

转载于:https://www.cnblogs.com/geng4512/p/5296893.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值