文本生成器

求可行方案数,可能容斥,但是操作过于complex,复杂度爆炸,不可做。

由于总方案数一定,为26^m,求不可行方案数,相减即可。此时的不可行方案数模型为求使一个字符串不含任何单词的方案数。

那么我们定义dp[i][j],表示走i步(即路径长度为i),到达Ac_automaton上第j个节点的方案数(应该是小套路),那么我们先给出没有细节限制的状态转移方程:

dp[i][k]+=dp[i-1][j];(k是j的儿子节点)

那么我们想一下实现细节,首先我们的方程应该是不经过任何单词的方案数,所以在单词尾打标记是一定的,同时根据以往Ac_automaton的经验,这个单词被标记,则fail指向它的那些节点也应该被标记,因为该单词是其他单词的后缀,一旦其他单词出现,则该单词一定出现,不符合转移要求,所以要打上标记。

然后在上述方程中,我们计算贡献时是不能经过任何单词的,所以一旦枚举到有标记的节点直接continue掉。

最后统计答案时,有标记的节点不应该作出贡献,可能有同学会问,我上面不是没更新有标记节点吗?他们不是0?实际上,我们只是没让这些节点去作出贡献,而有的贡献已经转移到他们身上(自己去观察一下方程)。

ans=26^m-sigma(dp[m][i])。

不要忘了取模法则。+个mod防负数。

#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<vector>
#include<queue>
#include<map>
using namespace std;
const int mod=10007;
int n,m;
char s[200];
struct Ac_automaton{
    int tot,root,ans,cnt;
    int trie[16000][26],fail[16000],v[16000];
    int dp[200][16000];
    void clear(){tot=root=ans=1;cnt=0;}
    void insert(){
        int l=strlen(s+1);
        int now=root;
        for(int i=1;i<=l;i++){
            if(!trie[now][s[i]-'A']) trie[now][s[i]-'A']=++tot;
            now=trie[now][s[i]-'A'];
        }v[now]=1;
    }
    void generate(){
        queue<int>q;
        for(int i=0;i<26;i++)
            if(trie[root][i]){
                fail[trie[root][i]]=root;
                q.push(trie[root][i]);
            }else trie[root][i]=root;
        while(!q.empty()){
            int now=q.front();
            q.pop();
            for(int i=0;i<26;i++)
                if(trie[now][i]){
                    fail[trie[now][i]]=trie[fail[now]][i];
                    v[trie[now][i]]|=v[fail[trie[now][i]]];
                    q.push(trie[now][i]);
                }else trie[now][i]=trie[fail[now]][i];
        }
    }
    int Dp(){
        dp[0][1]=1;
        for(int i=1;i<=m;i++)
            for(int j=1;j<=tot;j++){
                if(v[j]) continue;
                for(int k=0;k<26;k++)
                    dp[i][trie[j][k]]=(dp[i][trie[j][k]]+dp[i-1][j])%mod;
            }
        for(int i=1;i<=tot;i++)
            if(!v[i]) cnt=(cnt+dp[m][i])%mod;
        for(int i=1;i<=m;i++)
            ans=(ans*26)%mod;
        return (ans%mod-cnt%mod+mod)%mod;
    }
}Acm;
int main(){
    scanf("%d%d",&n,&m);
    Acm.clear();
    for(int i=1;i<=n;i++){
        scanf("%s",s+1);
        Acm.insert();
    }Acm.generate();
    printf("%d",Acm.Dp());
    return 0;
}
View Code

猜你喜欢

转载自www.cnblogs.com/Yu-shi/p/11072960.html