Bzoj 3530 数数

[1,n](n101000) 中不包含给定数字作为字串的数字个数 (mod109+7) ,给定数字总长度 1500


一眼看过去是一个用AC自动机表示状态,用数位dp的模板题

注意前导零是不会被统计到的

具体见代码

#include<bits/stdc++.h>
using namespace std;

const int maxn = 1600,maxLen = 10;
const int mod = 1e9+7;

#define LL long long 

queue<int> Q;
int toid(char c){ return c - '0'; }

struct acam{
    int nex[maxn][maxLen],fail[maxn];
    int cnt[maxn];
    int _cnt,root;
    int newNode(){
        memset(nex[_cnt],fail[_cnt] = -1,sizeof(nex[_cnt]));
        cnt[_cnt] = 0;
        return _cnt++;
    }
    void init(){
        _cnt = 0;
        root = newNode();
    }
    void insert(char *arr){
        int st = root;
        for(int i=0;arr[i];i++){
            int & stx = nex[st][toid(arr[i])]; 
            if(stx == -1) stx = newNode();
            st = stx;
        }
        cnt[st]++;
    }
    void build(){
        while(Q.empty()==false) Q.pop();
        fail[root] = root;
        int st;
        for(int i=0;i<maxLen;i++){
            if((st = nex[root][i]) != -1){
                fail[st] = root;
                Q.push(st);
            }
            else nex[root][i] = root;
        }
        while(Q.empty()==false){
            st = Q.front(),Q.pop();
            for(int i=0;i<maxLen;i++){
                if(nex[st][i] != -1){
                    int fst = fail[st],son = nex[st][i];
                    while(fst != root && nex[fst][i] == -1)
                        fst = fail[fst];
                    fail[son] = nex[fst][i] == -1 ? root : nex[fst][i]; 
                    Q.push(son);
                }
                else{
                    nex[st][i] = nex[fail[st]][i];
                }
            }
        }
    }
}acam;

int num[maxn];
LL dp[maxn][2][maxn][2];

LL dfs(int pos,bool bnd,int st,int az){
    if(pos < 0) return 1;
    LL & ndp = dp[pos][bnd][st][az];
    if(~ndp) return ndp;
    ndp = 0;
    int bound = bnd ? num[pos] : 9;
    for(int i=0;i<=bound;i++){
        if(az && i == 0){
            (ndp += dfs(pos-1,bnd && i == bound,st,az && i == 0))%= mod;
        }
        else{
            int nex = acam.nex[st][i];
            if(acam.cnt[nex] == 0)
                (ndp += dfs(pos-1,bnd && i == bound,nex,az && i == 0)) %= mod;
        }
    }
    return ndp;
}

char arr[2000];
int main(){
    scanf("%s",arr);
    int len = strlen(arr);
    reverse(arr,arr+len);
    for(int i=0;i<len;i++) num[i] = toid(arr[i]);
    int m;
    scanf("%d",&m);
    acam.init();
    while(m--){
        scanf("%s",arr);
        acam.insert(arr);
    }
    acam.build();
    memset(dp,-1,sizeof(dp));
    printf("%lld\n",(dfs(len-1,true,0,true)-1+mod) % mod);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值