文本生成器(AC自动机+dp)

Posted WindZR

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了文本生成器(AC自动机+dp)相关的知识,希望对你有一定的参考价值。

F. 文本生成器
内存限制:512 MiB 时间限制:1000 ms 标准输入输出
题目类型:传统 评测方式:文本比较

题目描述

原题来自:JSOI 2007

JSOI 交给队员 ZYX 一个任务,编制一个称之为「文本生成器」的电脑软件:该软件的使用者是一些低幼人群,他们现在使用的是 GW 文本生成器 v6 版。该软件可以随机生成一些文章――总是生成一篇长度固定且完全随机的文章——也就是说,生成的文章中每个字节都是完全随机的。如果一篇文章中至少包含使用者们了解的一个单词,那么我们说这篇文章是可读的(我们称文章包含单词 ,当且仅当单词 是文章的子串)。

但是,即使按照这样的标准,使用者现在使用的 GW 文本生成器 v6 版所生成的文章也是几乎完全不可读的。ZYX 需要指出 GW 文本生成器 v6 生成的所有文本中可读文本的数量,以便能够成功获得 v7 更新版。你能帮助他吗?
输入格式

输入的第一行包含两个正整数,分别是使用者了解的单词总数N,GW 文本生成器 v6 生成的文本固定长度M;以下N行,每一行包含一个使用者了解的单词。
输出格式

一个整数,表示可能的文章总数。只需要知道结果模1e4+7

的值。
样例
样例输出

2 2
A
B

样例输出

100

数据范围与提示

对于全部数据,N<=60,所有单词及文本的长度不会超过100 ,并且只可能包含英文大写字母。

析:求合法的方案数,如果直接求那么比较难搞,那么我们就用单步容斥,合法=总数-不合法,而总数很显然为 26^m , 现在问题就变成了求不合法的方案数,那么我们就要在每个单词末尾打一个标记,不合法的就是没有标记的单词,

Refun:学长说AC自动机的DP都非常套路

大部分f[i][j]表示当前在节点j,且串长为i时的情况,

有时再加一维表示这个状态里面包含了哪些东西

而且AC自动机的DP会经常让你用矩阵乘法优化 
那么行吧,我们就设 f[i][j] 表示当前长度为 i ,所在节点为 j 的不合法的方案数
那么 f[i][use[j].son[k]]+=f[i-1][j];
计算过程中注意取模

#include<bits/stdc++.h>
#define re register int
#define int long long
using namespace std;
const int mo=1e4+7;
int n,m,ans,sum,cnt=1;
char s[1100];
int f[210][70*26*20];
queue<int>q;
struct CUN
{
    int fail,flag;
    int son[30];
}use[70*26*20];
void insert(char ss[])
{
    int l=strlen(ss);
    int now=1;
    for(re i=0;i<l;i++)
    {
        int p=ss[i]-\'A\';
        if(!use[now].son[p])
            use[now].son[p]=++cnt;
        now=use[now].son[p];
    }
    use[now].flag=1;
}
void get_fail()
{
    for(re i=0;i<26;i++)
        use[0].son[i]=1;
    use[1].fail=0;
    q.push(1);
    while(!q.empty())
    {
        int u=q.front();
        int Fail=use[u].fail;
        q.pop();
        for(re i=0;i<26;i++)
        {
            int v=use[u].son[i];
            if(!v)
            {
                use[u].son[i]=use[Fail].son[i];
                continue;
            }
            use[v].fail=use[Fail].son[i];
            if(use[use[v].fail].flag)
                use[v].flag|=use[use[v].fail].flag;
            q.push(v);
        }
    }
}
int ksm(int d,int z)
{
    int out=1;
    while(z)
    {
        if(z&1)
            out=out*d%mo;
        z>>=1;
        d=d*d%mo;
    }
    return out;
}
signed main()
{   
    cin>>n>>m;
     for(re i=1;i<=n;i++)
     {
         cin>>s;
         insert(s);
     }
     get_fail();
    f[0][1]=1;
     for(re i=1;i<=m;i++)
     {
         for(re j=1;j<=cnt;j++)
         {
             for(re k=0;k<26;k++)
             {
                 if(!use[use[j].son[k]].flag)
                 {
                     f[i][use[j].son[k]]=(f[i][use[j].son[k]]%mo+f[i-1][j]%mo)%mo;
                 }
             }
         }
     }
     ans=ksm(26,m);
     for(re i=1;i<=cnt;i++)
        ans=(ans-f[m][i]+mo)%mo;
    printf("%lld\\n",(ans%mo+mo)%mo);
}

以上是关于文本生成器(AC自动机+dp)的主要内容,如果未能解决你的问题,请参考以下文章

文本生成器(AC自动机+dp)

[JSOI2007]文本生成器 [AC自动机,dp]

bzoj1030 文本生成器(AC自动机+dp)

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

bzoj1030: [JSOI2007]文本生成器(AC自动机+DP)

[JSOI2007]文本生成器(AC自动机+DP)