文本生成器(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)的主要内容,如果未能解决你的问题,请参考以下文章
bzoj1030 [JSOI2007]文本生成器——AC自动机+DP