【jzoj5332】【NOIP2017提高A组模拟8.23】【密码】【ac自动机】【动态规划】

66 篇文章 0 订阅
23 篇文章 1 订阅

description

这里写图片描述

solution

先把秘钥建ac自动机,设f[i][j][k][l]表示现在填到第i位,对应ac自动机上j结点,包含k个秘钥,有没有顶上界,枚举下一个填什么转移即可。

code

#include<set>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define LL long long
#define fo(i,j,k) for(int i=j;i<=k;i++)
#define fd(i,j,k) for(int i=j;i>=k;i--)
#define fr(i,j) for(int i=begin[j];i;i=next[i])
using namespace std;
int const mn=2*1e5+9,ml=500+9,mo=1e9+7;
int n,K,pon,fail[ml],cnt[ml],son[ml][10],f[ml][ml][12][2],q[ml];
char s[ml],s1[ml],s2[ml];
int min(int x,int y){
    return (x<y)?x:y;
}
int calc(char *s){
    int m=strlen(s+1);
    fo(i,0,m)fo(j,0,pon)fo(k,0,K)fo(l,0,1)f[i][j][k][l]=0;
    f[0][0][0][1]=1;
    fo(i,0,m-1)fo(j,0,pon)fo(k,0,K)fo(l,0,1)if(f[i][j][k][l]){
        int lim=(l)?s[i+1]-'0':9;
        fo(ii,0,lim)
            f[i+1][son[j][ii]][min(k+cnt[son[j][ii]],K)][l&&(ii==s[i+1]-'0')]
                =(f[i+1][son[j][ii]][min(k+cnt[son[j][ii]],K)][l&&(ii==s[i+1]-'0')]
                    +f[i][j][k][l])%mo;
    }
    LL ans=0;
    fo(j,0,pon)ans=(ans+f[m][j][K][0]+f[m][j][K][1])%mo;
    return ans;
}
int main(){
    freopen("word.in","r",stdin);
    freopen("word.out","w",stdout);
    scanf("%d%d",&n,&K);
    scanf("%s",s1+1);
    scanf("%s",s2+1);
    fo(cas,1,n){
        scanf("%s",s+1);
        int p=0,m=strlen(s+1);
        fo(i,1,m){
            int ch=s[i]-'0';
            if(!son[p][ch])son[p][ch]=++pon;
            p=son[p][ch];
        }
        cnt[p]++;
    }
    int he=0,ti=0;
    fo(i,0,9)if(son[0][i])q[++ti]=son[0][i];
    while(he!=ti){
        int p=q[++he];
        fo(i,0,9)if(son[p][i]){
            int u=q[++ti]=son[p][i];
            fail[u]=fail[p];
            while(fail[u]&&(!son[fail[u]][i]))fail[u]=fail[fail[u]];
            fail[u]=son[fail[u]][i];
            cnt[u]+=cnt[fail[u]]+cnt[p];
        }
    }
    fo(i,0,pon)fo(j,0,9)if(!son[i][j]){
        int p=fail[i];
        while(p&&(!son[p][j]))
            p=fail[p];
        son[i][j]=son[p][j];
    }
    printf("%d",((calc(s2)-calc(s1))%mo+mo)%mo);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值