【JSOI2007】文本生成器
【JSOI2007】文本生成器
by AmanoKumiko
Description
JSOI 交给队员 ZYX 一个任务,编制一个称之为「文本生成器」的电脑软件:该软件的使用者是一些低幼人群,他们现在使用的是 GW 文本生成器 v6 版。该软件可以随机生成一些文章――总是生成一篇长度固定且完全随机的文章——也就是说,生成的文章中每个字节都是完全随机的。如果一篇文章中至少包含使用者们了解的一个单词,那么我们说这篇文章是可读的(我们称文章\(a\)包含单词\(b\),当且仅当单词\(b\)是文章\(a\)的子串)。
但是,即使按照这样的标准,使用者现在使用的 GW 文本生成器 v6 版所生成的文章也是几乎完全不可读的。ZYX 需要指出 GW 文本生成器 v6 生成的所有文本中可读文本的数量,以便能够成功获得 v7 更新版。你能帮助他吗?
Input
输入的第一行包含两个正整数,分别是使用者了解的单词总数\(N\),GW 文本生成器 v6 生成的文本固定长度\(M\);
以下\(N\)行,每一行包含一个使用者了解的单词。
Output
一个整数,表示可能的文章总数。只需要知道结果模\(10007\)的值。
Sample Input
2 2
A
B
Sample Output
100
Data Constraint
对于全部数据\(1<=N<=60\),所有单词及文本的长度不会超过100,并且只可能包含英文大写字母。
Solution
正难则反
1.建自动机
2.给可能经过单词结尾的位置打上标记
3.设\(f[i][j]\)表示当前长度为\(j\),在自动机上第\(i\)个点的不合法方案数
4.\(ans=26^m-Σf[i][m]\)
Code
#include<bits/stdc++.h>
using namespace std;
#define F(i,a,b) for(int i=a;i<=b;i++)
#define Fd(i,a,b) for(int i=a;i>=b;i--)
#define mo 10007
#define N 70
#define L 110
#define P 6010
queue<int>q;
int n,m,ans=1,sum,f[P][L],len;
char ch[L];
struct ACM{
int son[P][26],fail[P],flag[P],tot,Q[P],he;
void insert(){
int u=1;
F(i,1,len)son[u][ch[i]-'A']?u=son[u][ch[i]-'A']:u=son[u][ch[i]-'A']=++tot;
flag[u]=1;
}
void build(){
F(i,0,25)son[0][i]=1;q.push(1);
while(!q.empty()){
int u=q.front();q.pop();Q[++he]=u;
F(i,0,25){
int v=son[u][i],Fail=fail[u];
if(!v){son[u][i]=son[Fail][i];continue;}
fail[v]=son[Fail][i];
flag[v]|=flag[u];
q.push(v);
}
}
F(i,1,he)flag[Q[i]]|=flag[fail[Q[i]]];//标记
}
}t;
int main(){
scanf("%d%d",&n,&m);t.tot=1;
F(i,1,n)scanf("%s",ch+1),len=strlen(ch+1),t.insert();
t.build();
f[1][0]=1;
F(j,0,m-1) F(i,1,t.tot)if(f[i][j]){F(k,0,25)if(!t.flag[t.son[i][k]])(f[t.son[i][k]][j+1]+=f[i][j])%=mo;}
F(i,1,t.tot)(sum+=f[i][m])%=mo;//dp
F(i,1,m)(ans*=26)%=mo;
printf("%d",(ans-sum+mo)%mo);
return 0;
}