[BZOJ1030][JSOI2007]文本生成器

题目描述 Description

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

输入描述 Input Description

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

输出描述 Output Description
  一个整数,表示可能的文章总数。只需要知道结果模10007的值。
样例输入 Sample Input
2 2
A
B
样例输出 Sample Output
100
数据范围及提示 Data Size & Hint
 

 肯定是一个AC自动机类题。典型的AC自动机上DP类题。

首先把这些单词都放在一棵Trie树上,单词结尾打一下danger标记,本题的题目意思就是说在AC自动机上走M步,使得每一步都不走在danger节点的方案数。

那么我们就开始DP 设f(i,j)表示现在在第i号点,走了j步的方案数。转移的话就比较简单了。f(i,j)可以转移到f(ch[i][k],j+1),前提是ch[i][k]不是危险节点。本题我用的是新版AC自动机,对每一个转移都一视同仁,不用管什么走Fail边。

 1 #include<iostream>
 2 #include<algorithm>
 3 #include<cstdio>
 4 #include<queue>
 5 #include<cmath>
 6 #include<cstring>
 7 using namespace std;
 8 typedef long long LL;
 9 typedef pair<int,int> PII;
10 #define mem(a,b) memset(a,b,sizeof(a))
11 inline int read()
12 {
13     int x=0,f=1;char c=getchar();
14     while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
15     while(isdigit(c)){x=x*10+c-'0';c=getchar();}
16     return x*f;
17 }
18 const int maxn=6010,MOD=10007;
19 int n,m,tot,ch[maxn][30],Fail[maxn],danger[maxn],dp[110][maxn],ans,sum=1;
20 bool vis[110][maxn];
21 char s[110];
22 int id(char c){return c-'A';}
23 void insert(char *s)
24 {
25     int len=strlen(s),now=0;
26     for(int i=0;i<len;i++)
27     {
28         int c=id(s[i]);
29         if(!ch[now][c])ch[now][c]=++tot;
30         now=ch[now][c];
31     }
32     danger[now]=1;
33 }
34 void getFail()
35 {
36     queue<int> Q;
37     for(int i=0;i<26;i++)if(ch[0][i])Q.push(ch[0][i]);
38     while(Q.size())
39     {
40         int u=Q.front();Q.pop();
41         for(int i=0;i<26;i++)
42         {
43             int &v=ch[u][i];
44             if(!v){v=ch[Fail[u]][i];continue;}
45             Q.push(v);
46             int j=Fail[u];
47             while(j && !ch[j][i])j=Fail[j];
48             Fail[v]=ch[j][i];
49             danger[v]|=danger[Fail[v]];
50         }
51     }
52 }
53 int main()
54 {
55     n=read();m=read();
56     for(int i=0;i<n;i++)scanf("%s",s),insert(s);
57     getFail();
58     dp[0][0]=1;
59     for(int i=1;i<=m;i++)
60         for(int j=0;j<=tot;j++)
61             if(!danger[j])
62                 for(int k=0;k<26;k++)
63                 {
64                     int &v=ch[j][k];
65                     if(!danger[v])dp[i][v]=(dp[i][v]+dp[i-1][j])%MOD;
66                 }
67     for(int i=0;i<=tot;i++)if(!danger[i])ans=(ans+dp[m][i])%MOD;
68     for(int i=1;i<=m;i++)sum=(sum*26)%MOD; 
69     printf("%d
",(sum-ans+8*MOD)%MOD);
70     return 0;
71 }
View Code