• 文本生成器


    https://loj.ac/problem/10063

    题目描述

      给出(N)个单词和文本长度(M),求有多少文本满足其内至少包含一个单词,答案对(10007)取余。

    思路

      直接求满足的文本比较困难,我们考虑求答案的补集,也就是不包含任何一个单词的文本串的数量。对于这个答案我可以用(dp)求解,但考虑对单词的查找我们需要用(A)C自动机解,因此题目就比较明显了,(AC)自动机上跑(dp),我们用(dp[i][j])表示文本长度为(i),文本的最后一个字符位于(Trie)图上的节点编号为(j)时的方案数。所以转移方程就比较简单了,(dp[i+1][u]+=dp[i][v])((u)能从(v)转移过来),而这个方程的求法与区间(dp)类似,我们第一重枚举长度,因为每一个解必定由更短的长度转移过来。

      不过有点必须阐明,我们虽然在(dp)方程中有一维枚举的是(Trie)图上的节点编号,实际还是每一个字母,但有一个类似匹配的过程,因此这个(dp)方程是正确的,它必定不会包含重复的状态,也就可以求出正确答案。

    代码

    #include<bits/stdc++.h>
    using namespace std;
    const int mod=10007;
    
    int ch[6100][26],tot=1;
    bool ed[6100];
    void insert(char *s)
    {
        int u=1,len=strlen(s);
        for(int i=0;i<len;i++)
        {
            int c=s[i]-'A';
            if(!ch[u][c])ch[u][c]=++tot;
            u=ch[u][c];
        }
        ed[u]=1;
    }
    
    int qpow(int a,int b)
    {
        int res=1;
        for(;b;b>>=1)
        {
            if(b&1)res=res*a%mod;
            a=a*a%mod;
        }
        return res;
    }
    
    int nxt[6100];
    void getfail()
    {
        for(int i=0;i<26;i++)
            ch[0][i]=1;
        queue<int>q;
        q.push(1);nxt[1]=0;
        while(!q.empty())
        {
            int u=q.front();q.pop();
            for(int i=0;i<26;i++)
            {
                if(!ch[u][i])ch[u][i]=ch[nxt[u]][i];
                else
                {
                    int v=nxt[u];
                    q.push(ch[u][i]);
                    while(v&&!ch[v][i])v=nxt[v];
                    nxt[ch[u][i]]=ch[v][i];
                }
            }
        }
        for(int i=1;i<=tot;i++)
        {
            int v=nxt[i];
            while(v)ed[i]|=ed[v],v=nxt[v];        //对所有节点的ed标记进行传递 
        }
    }
    
    int dp[110][6010];
    char s[110];
    int main()
    {
        int n,m;
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
        {
            scanf(" %s",s);
            insert(s);
        }
        getfail();
        dp[0][1]=1;
        for(int i=0;i<m;i++)
            for(int j=1;j<=tot;j++)
                for(int k=0;k<26;k++)
                    if(!ed[j]&&!ed[ch[j][k]])dp[i+1][ch[j][k]]=(dp[i+1][ch[j][k]]+dp[i][j])%mod;//枚举的两个点必定不是单词结尾 
        int ans=qpow(26,m);
    //    for(int i=1;i<=tot;i++)
    //        cout<<dp[m][i]<<endl;
        for(int i=1;i<=tot;i++)
            ans=(ans-dp[m][i]+mod)%mod;
        printf("%d",ans);
    }
    
  • 相关阅读:
    socket
    netstat
    列表
    突然发现不会写代码了
    算法资源
    bit位操作
    排序算法
    连续子数组最大和
    books
    凸优化
  • 原文地址:https://www.cnblogs.com/fangbozhen/p/11794748.html
Copyright © 2020-2023  润新知