bzoj1030 [JSOI2007]文本生成器——AC自动机+DP

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1030

求至少有一个单词的文本串不太好求,所以转化成求所有情况减去没有一个单词的文本串;

没有一个单词的文本串可以用AC自动机+DP求,设 f[i][j] 表示文本串长度为 i ,当前 Trie 树上节点为 j 的方案数;

则 f[i][j] 可以转到仍然不包含单词的它的儿子的方案数中,同时文本串长度+1;

所以需要在 getfail 时把单词结尾的属性也转移一下,因为 fail 是单词结尾的话,自己也一定有一个后缀是单词结尾,也就是自己这里也包含单词了;

最后统计答案就是所有的 f[m][i] 的和,其中 i 可以是 Trie 树上任意一个点。

代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
int const mod=10007;
int n,m,f[105][6005],cnt,ans;
char s[105];
queue<int>q;
struct N{int son[30],fail,end;}t[6005];
void build()
{
    int l=strlen(s),nw=0;
    for(int i=0;i<l;i++)
    {
        if(!t[nw].son[s[i]-'A'])t[nw].son[s[i]-'A']=++cnt;
        nw=t[nw].son[s[i]-'A'];
    }
    t[nw].end=1;
}
void getfail()
{
    t[0].fail=0;
    for(int i=0;i<26;i++)
        if(t[0].son[i])
        {
            t[t[0].son[i]].fail=0;
            q.push(t[0].son[i]);
        }
    while(q.size())
    {
        int x=q.front(); q.pop();
        for(int i=0;i<26;i++)
        {
            if(t[x].son[i])
            {
                t[t[x].son[i]].fail=t[t[x].fail].son[i];
//                t[t[x].son[i]].end|=t[t[t[x].son[i]].fail].end;
                q.push(t[x].son[i]);
            }
            else t[x].son[i]=t[t[x].fail].son[i];
        }
        if(t[t[x].fail].end)t[x].end=1;
    }    
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        cin>>s;
        build();
    }
    getfail();
    f[0][0]=1;//!
    for(int i=0;i<m;i++)
        for(int nw=0;nw<=cnt;nw++)
        {
            if(t[nw].end||!f[i][nw])continue;//
            for(int j=0;j<26;j++)
            {
                int x=t[nw].son[j];
                (f[i+1][x]+=f[i][nw])%=mod;
            }
        }
    ans=1;
    for(int i=1;i<=m;i++) (ans*=26)%=mod;
    for(int i=0;i<=cnt;i++) 
        if(t[i].end==0)ans=((ans-f[m][i])%mod+mod)%mod; //end=0!
    printf("%d",ans);
    return 0;
}
原文地址:https://www.cnblogs.com/Zinn/p/9325671.html