zoj 3494 BCD Code AC自动机 + 数位dp

/************
AC自动机 + 数位dp
好题!

给出一些模式串,求区间内不出现这些串的数字串的个数
以这些模式串构建AC自动机,
建自动机的时候要注意记录好结束状态

预处理出所有状态加上某个数字后的状态,在数位枚举每一位上数字的
时候,直接推出下一个状态。
特别要注意的是前导0的情况,当前面全是0的时候而且当前位也是0的时候,
就可以当成初始状态来处理。
****************/
#include <cstdio>
#include <queue>
#include <cstring>
#include <iostream>
using namespace std;
#define maxn 500010

struct Trie{
    int next[maxn][2],fail[maxn];
    bool end[maxn];
    int root,L;

    int newNode(){
        for(int i=0;i<2;i++) next[L][i] = -1;
        end[L++] = false;
        return L-1;
    }

    void init(){
        L = 0;
        root = newNode();
    }

    void insert(char str[]){
        //printf("insert:%s\n",str);
        int len = strlen(str);
        int now = root;
        for(int i=0;i<len;i++){
            int temp = str[i] - '0';
            if(next[now][temp] == -1){
                next[now][temp] = newNode();
            }
            now = next[now][temp];
        }
        end[now] = true;
    }

    void build(){
        queue<int> que;
        fail[root] = root;
        for(int i=0;i<2;i++){
            if( next[root][i] == -1){
                next[root][i] = root;
            }else{
                fail[next[root][i]] = root;
                que.push(next[root][i]);
            }
        }
        while(!que.empty()){
            int now = que.front();
            que.pop();
            if(end[fail[now]]) end[now] = true;
            for(int i=0;i<2;i++){
                int t = next[now][i];
                if(t==-1){
                    next[now][i] = next[fail[now]][i];
                }else{
                    fail[t] = next[fail[now]][i];
                    //printf("id:%d fail:%d\n",t,fail[t]);
                    que.push(t);
                }
            }
        }
    }

    void debug(){
        for(int i=0;i<L;i++){
            printf("id:%3d fail:%3d end:%3d next:[",i,fail[i],end[i]);
            for(int j=0;j<26;j++){
                printf(" %2d",next[i][j]);
            }
            printf("]\n");
        }
    }
}ac;

typedef long long LL;
const int mod = 1000000009;
char buf[250];
int next[2010][20];
LL dp[250][2010];
int p[250];

int change(int s,int k){
    if(ac.end[s]) return -1;
    for(int i=3;i>=0;i--){
        if(ac.end[ac.next[s][(k>>i)&1]]) return -1;
        s = ac.next[s][(k>>i)&1];
    }
    return s;
}

void pre_solve(){
    for(int i=0;i<ac.L;i++){
        for(int j=0;j<10;j++){
            next[i][j] = change(i,j);
            //printf("i:%d j:%d %d\n",i,j,next[i][j]);
        }
    }
}

LL dfs(int h,int s,bool flag, bool zero){
    if(h==-1) return 1;
    if(!flag && dp[h][s]!=-1) return dp[h][s];
    LL res = 0;

    if(zero){
        res += dfs(h-1,s,flag && p[h]==0,true);
        res %= mod;
    }else{
        if(next[s][0]!=-1) res += dfs(h-1,next[s][0], flag && p[h]==0, false);
        res %= mod;
    }

    int up = flag?p[h]:9;

    for(int i=1;i<=up;i++){
        if(next[s][i] == -1) continue;
        res += dfs(h-1,next[s][i],flag&&i==up,false);
        res %= mod;
    }

    if(!flag && !zero) dp[h][s] =  res;
    return res;
}

LL solve(char s[]){
    int len = strlen(s);
    for(int i=0;i<len;i++) p[i] = s[len-i-1] - '0';
    return dfs(len-1,0,true,true);
}

int main(){
    int T,n;
    scanf("%d",&T);
    while(T--){
        scanf("%d",&n);
        ac.init();
        memset(dp,-1,sizeof(dp));
        while(n--){
            scanf("%s",buf);
            ac.insert(buf);
        }
        ac.build();
       // ac.debug();
        pre_solve();

        scanf("%s",buf);
        int len = strlen(buf);
        for(int i=len-1;i>=0;i--){
            if(buf[i] == '0') buf[i] = '9';
            else {
                buf[i]--;
                break;
            }
        }
        LL ans = -solve(buf);
        scanf("%s",buf);
        ans += solve(buf);
        ans = (ans%mod + mod)%mod;
        cout << ans << endl;
    }
    return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值