0
点赞
收藏
分享

微信扫一扫

BZOJ 1030 [JSOI2007]文本生成器 DP+AC自动机


Description


  JSOI交给队员ZYX一个任务,编制一个称之为“文本生成器”的电脑软件:该软件的使用者是一些低幼人群,
他们现在使用的是GW文本生成器v6版。该软件可以随机生成一些文章―――总是生成一篇长度固定且完全随机的文
章—— 也就是说,生成的文章中每个字节都是完全随机的。如果一篇文章中至少包含使用者们了解的一个单词,
那么我们说这篇文章是可读的(我们称文章a包含单词b,当且仅当单词b是文章a的子串)。但是,即使按照这样的
标准,使用者现在使用的GW文本生成器v6版所生成的文章也是几乎完全不可读的?。ZYX需要指出GW文本生成器 v6
生成的所有文本中可读文本的数量,以便能够成功获得v7更新版。你能帮助他吗?


Input


  输入文件的第一行包含两个正整数,分别是使用者了解的单词总数N (<= 60),GW文本生成器 v6生成的文本固
定长度M;以下N行,每一行包含一个使用者了解的单词。这里所有单词及文本的长度不会超过100,并且只可能包
含英文大写字母A..Z


Output


  一个整数,表示可能的文章总数。只需要知道结果模10007的值。


Sample Input


2 2
A
B


Sample Output


100


HINT



Source




​​题目链接​​


题目看上去比较……难……但其实想通了就好。


首先无所畏惧把所有要求的串(s{})放到trie树里面,


然后目标是求含有至少一个的数目,,直接求可能比较困难。。


不是可能,而是很困难。


那么根据容斥原理,可以化成:不含任何一个的数目。


这个看上去就可以写了,然后来求解。




首先能够想到AC自动机;


因为目标是判断出一个串是否包含在了文章内。


我们要避免所有单词,就在原来有单词的trie的位置上标上一个标记。


然后如何计算?思考一下dp。




状态dp[u][v]表示走了u步了(已经u个字母)且在trie上位置为v的方案数。


我们知道它可以转移到dp[u+1][trie[v][k]],其中k为'A'~'Z'中的所有值。


但是如果trie[v][k]=0,我们得看看fail指针的位置有没有可以更新的地方。


这个也就是讨厌处(一开始忘记了……)


其实在处理fail指针,BFS的时候就可以直接把所有指针反映到trie里面了:




if (trie[now][u]==0) trie[now][u]=trie[fail[now]][u]; else……




然后就是一个简单的dp了……


注意0号点算上……必须的……


dp的边界?


显然地,起点是0,然后走0步,dp[0][0]=1.


最后统计答案?


在有标记的地方,说明包含了单词,我们不能统计这个答案;


不然累计所有的dp[M][i]即可,其中M是文章长度。




当然最后的答案根据容斥原理,可以得到:


26^M-X


X是上述dp统计出来的答案。




这题比较经典了吧……






#include<bits/stdc++.h>
using namespace std;
int read(){
int x=0,f=1;char ch=getchar();
while (ch<'0' || ch>'9'){if (ch=='-') f=-1;ch=getchar();}
while (ch>='0' && ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
const int
moder=10007,
MaxLen=105,
N=65;
char s[MaxLen];
bool words[MaxLen*N];
int n,Len,cnt;
int trie[MaxLen*N][26];
int fail[MaxLen*N],Q[MaxLen*N];
int f[MaxLen][MaxLen*N];
int find(int x,int t){
if (trie[x][t]) return trie[x][t];
if (!x) return 0;
return find(fail[x],t);
}
void insert(){
int len=strlen(s),now=0,j;
for (int i=0;i<len;i++){
j=s[i]-'A';
if (trie[now][j]) now=trie[now][j];
else now=trie[now][j]=++cnt;
}
words[now]=1;
}
void AC(){
int head=0,tail=1;
Q[0]=0; int now,j;
while (head!=tail){
now=Q[head++];
words[now]|=words[fail[now]];
for (int i=0;i<26;i++){
if (!trie[now][i]){
trie[now][i]=trie[fail[now]][i];
continue;
}
j=find(fail[now],i);
if (!now) j=0;
fail[trie[now][i]]=j;
Q[tail++]=trie[now][i];
}
}
}
int solve(){
f[0][0]=1;
for (int i=1;i<=Len;i++)
for (int j=0;j<=cnt;j++){
if (words[j] || !f[i-1][j]) continue;
for (int k=0;k<26;k++)
f[i][trie[j][k]]=(f[i][trie[j][k]]+f[i-1][j])%moder;
}
int ans1=0,ans2=1;
for (int i=0;i<=cnt;i++)
if (!words[i]) ans1=(ans1+f[Len][i])%moder;
for (int i=1;i<=Len;i++)
ans2=(ans2*26)%moder;
return (ans2-ans1+moder)%moder;
}
int main(){
n=read(),Len=read();
cnt=0;
for (int i=1;i<=n;i++){
scanf("%s",s);
insert();
}
AC();
printf("%d\n",solve());
return 0;
}


举报

相关推荐

0 条评论