文本生成器

♀尐吖头ヾ 提交于 2019-12-01 02:21:28

https://loj.ac/problem/10063

题目描述

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

思路

  直接求满足的文本比较困难,我们考虑求答案的补集,也就是不包含任何一个单词的文本串的数量。对于这个答案我可以用dp求解,但考虑对单词的查找我们需要用AC自动机解,因此题目就比较明显了——AC自动机上跑dp,我们用dp[i][j]表示文本长度为i,文本的最后一个字符位于Trie图上的节点编号时的方案数。所以转移方程就比较简单了,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);
}

 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!