【Luogu】P3311数数(AC自动机上DP)

  题目链接

  蒟蒻今天终于学会了AC自动机,感觉很稳

  (后一句愚人节快乐)

  这题开一个f[i][j][k]表示有没有受到限制,正在枚举第j位,来到了AC自动机的第k个节点

  的方案数

  随后可以刷表更新

  注意如果是在枚举第一位的话注意前导0

  

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cctype>
#include<cstdlib>
#include<queue>
#define maxl 2000
#define maxu 10
#define mod 1000000007
using namespace std;
inline long long read(){
    long long num=0,f=1;
    char ch=getchar();
    while(!isdigit(ch)){
        if(ch=='-')    f=-1;
        ch=getchar();
    }
    while(isdigit(ch)){
        num=num*10+ch-'0';
        ch=getchar();
    }
    return num*f;
}

inline int count(int i){    return i-'0';    }

int tree[maxl*maxu][maxu];
int fail[maxl*maxu];
int val[maxl*maxu];
int tot;
char c[maxl];
char s[maxl];
long long f[3][maxl][maxl];
bool vis[maxl];


void update(){
    int n=strlen(c+1);    int now=0;
    for(int i=1;i<=n;++i){
        if(tree[now][count(c[i])]==0)    tree[now][count(c[i])]=++tot;
        now=tree[now][count(c[i])];
    }
    val[now]++;
    return;
}

void makefail(){
    queue<int>q;
    for(int i=0;i<10;++i)
        if(tree[0][i])    q.push(tree[0][i]);
    while(!q.empty()){
        int from=q.front();q.pop();
        for(int i=0;i<10;++i){
            if(tree[from][i]==0){
                tree[from][i]=tree[fail[from]][i];
                continue;
            }
            fail[tree[from][i]]=tree[fail[from]][i];
            val[tree[from][i]]|=val[tree[fail[from]][i]];
            q.push(tree[from][i]);
        }
    }
    return;
}

inline void add(long long &a,int b){
    a=(a+b)%mod;
}

int main(){
    scanf("%s",s+1);int n=strlen(s+1);
    int m=read();
    for(int i=1;i<=m;++i){
        scanf("%s",c+1);
        update();
    }
    long long ans=0;
    makefail();
    for(int i=0;i<n;++i)
        for(int j=0;j<=tot;++j){
            if(f[0][i][j]){
                int now=s[i+1]-'0';
                for(int k=0;k<now;++k){
                    int nxt=tree[j][k];
                    if(val[nxt]==0)    add(f[1][i+1][nxt],f[0][i][j]);
                }
                int nxt=tree[j][now];
                if(val[nxt]==0)    add(f[0][i+1][nxt],f[0][i][j]);
            }
            if(f[1][i][j]){
                for(int k=0;k<10;++k){
                    int nxt=tree[j][k];
                    if(val[nxt]==0)    add(f[1][i+1][nxt],f[1][i][j]);
                }
            }
            if(j==0){
                if(i==0){
                    int now=s[i+1]-'0';
                    for(int k=1;k<now;++k){
                        int nxt=tree[j][k];
                        if(val[nxt]==0)    add(f[1][i+1][nxt],1);
                    }
                    int nxt=tree[j][now];
                    if(val[nxt]==0)    add(f[0][i+1][nxt],1);
                }
                else{
                    for(int k=1;k<10;++k){
                        int nxt=tree[j][k];
                        if(val[nxt]==0)    add(f[1][i+1][nxt],1);
                    }
                }
            }
        }
    for(int i=0;i<=tot;++i){
        add(ans,f[0][n][i]);
        add(ans,f[1][n][i]);
    }
    ans=(ans+mod)%mod;
    printf("%lld\n",ans);
}

 

posted @ 2018-04-01 19:52  Konoset  阅读(189)  评论(0编辑  收藏  举报