1. 程式人生 > 實用技巧 >[AC自動機][dp][洛谷P4052][JSOI 2007] 文字生成器

[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;
}