[AC自动机][dp][洛谷P4052][JSOI 2007] 文本生成器

题目链接

题解

正难则反,我们可以去计算出不合法的字符串数量,然后用 \(26^m\) 减去不合法的字符串数量即为合法的字符串数量。发现计数时需要维护到枚举到字符串当前位置时的后缀,按照套路,这个东西可以放到AC自动机上来做。先把所有单词丢到AC自动机上,然后设 \(dp[i][p]\) 表示枚举到第 \(i\) 位,且当前后缀为trie树上的根结点到p结点的字符串,此时的不合法字符串总数。我们去枚举第 \(i+1\) 位放置的字符 \(x\),并设 \(T[p][x]\) 为AC自动机拓扑图上从 \(p\) 到达 \(x\) 的结点。那么有 \(dp[i+1][T[p][x]]+=dp[i][p]\)。注意枚举 \(x\) 时不能构造出给出的字符串,需要在构造 \(fail\) 指针时维护一下 \(ed\)。

Code

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

#define RG register int
#define LL long long

const int MOD=10007;

int ExPow(int b,int n){
    int x=1,Power=b%MOD;
    while(n){
        if(n&1) x=x*Power%MOD;
        Power=Power*Power%MOD;
        n>>=1;
    }
    return x;
}

template<size_t _size>
struct AC_automaton{
    static const int base=26;
    int T[_size][base],fail[_size],ed[_size],deep[_size];
    queue<int> Q;
    int cnt;

    void insert(char *s){
        int p=0;
        for(RG i=0;s[i];++i){
            int x=s[i]-'A';
            if(!T[p][x]){T[p][x]=++cnt;deep[cnt]=deep[p]+1;}
            p=T[p][x];
        }
        ed[p]=true;
    }
    void build(){
        for(RG x=0;x<base;++x)
            if(T[0][x]) Q.push(T[0][x]);
        while(!Q.empty()){
            int p=Q.front();Q.pop();
            for(RG x=0;x<base;++x){
                int v=T[p][x];
                if(v){
                    fail[v]=T[fail[p]][x];Q.push(v);
                    if(ed[fail[v]]||ed[p]) ed[v]=true;
                }
                else T[p][x]=T[fail[p]][x];
            }
        }
    }
};
AC_automaton<7000> AC;
char s[105];
int dp[101][6001];
int N,M;

int main(){
    scanf("%d%d",&N,&M);
    for(RG i=1;i<=N;++i){
        scanf("%s",s);
        AC.insert(s);
    }
    AC.build();
    dp[0][0]=1;
    for(RG i=0;i<M;++i){
        for(RG p=0;p<=AC.cnt;++p){
            if(AC.ed[p]) continue;
            for(RG x=0;x<AC.base;++x){
                int v=AC.T[p][x];
                if(AC.ed[v]) continue;
                dp[i+1][v]+=dp[i][p];
                if(dp[i+1][v]>=MOD) dp[i+1][v]%=MOD;
            }
        }
    }
    int Ans=0;
    for(RG p=0;p<=AC.cnt;++p){
        Ans+=dp[M][p];
        if(Ans>=MOD) Ans%=MOD;
    }
    Ans=((ExPow(26,M)-Ans)%MOD+MOD)%MOD;
    printf("%d\n",Ans);

    return 0;
}
上一篇:「JLOI2014」聪明的燕姿


下一篇:《字符串专题》