zoukankan      html  css  js  c++  java
  • 文本生成器

    求可行方案数,可能容斥,但是操作过于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
  • 相关阅读:
    SQL Server 2012 自动增长列,值跳跃问题(自增增加1000)
    根据城市表生成json数据
    LeetCode_257. Binary Tree Paths
    LeetCode_242. Valid Anagram
    LeetCode_237. Delete Node in a Linked List
    LeetCode_235. Lowest Common Ancestor of a Binary Search Tree
    LeetCode_234. Palindrome Linked List
    LeetCode_232. Implement Queue using Stacks
    LeetCode_231. Power of Two
    LeetCode_225. Implement Stack using Queues
  • 原文地址:https://www.cnblogs.com/Yu-shi/p/11072960.html
Copyright © 2011-2022 走看看