zoukankan      html  css  js  c++  java
  • [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
    ",Ans);
    
        return 0;
    }
    
  • 相关阅读:
    组装树状结构文本框
    在jsp页面动态添加,删除文本框模块
    sql字段拆分 ,连表子查询获取值
    jsp页面技术总结
    C语言的基本数据类型
    如何学习一些需要算法的程序
    如何学习一门新的编程语言
    C++ 预处理器
    C++ 函数模板&类模板
    C++ 文件和流
  • 原文地址:https://www.cnblogs.com/AEMShana/p/13769519.html
Copyright © 2011-2022 走看看