bzoj1030 [JSOI2007]文本生成器 - AC自动机+dp

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

 

题解:

题目中应该很好想到ac自动机来解决这道题,这里我们可以换一种思维方式,问知道的文章不好得出

结果,可以想有多少不认识的文章,就是说,给定的单词一个都不能出现。就是说如果该单词有就认

识,那么单词都要避开。

f[i][j]表示到了i这一位,总共m个,即长度,然后,到了j这一个点,可以获得的方案数。

那么就需要在建立Trie图的时候处理一下。

看这个垃圾图,x,y,z点,y指向z,z为标记节点,因为z为y的后缀

所以y包涵z,y也是不和法的,所以y也需要标记为不可到达。

然后就是转移了,复杂度可以压过。

 1 #include<cstring>
 2 #include<iostream>
 3 #include<algorithm>
 4 #include<cmath>
 5 #include<cstdio>
 6 #include<queue>
 7 #define mod 10007
 8 using namespace std;
 9 
10 int n,m,ans=1,res,cnt=1,f[107][6007];
11 struct Node
12 {
13     int c[26],suf;
14     bool flag;
15 }trie[6007];
16 char ch[107];
17 
18 void insert()
19 {
20     int now=1,len=strlen(ch);
21     for (int i=0;i<len;i++)
22     {
23         if (!trie[now].c[ch[i]-'A']) trie[now].c[ch[i]-'A']=++cnt;
24         now=trie[now].c[ch[i]-'A'];
25     }
26     trie[now].flag=true;
27 }
28 void make_AC()
29 {
30     for (int i=0;i<26;i++)
31         trie[0].c[i]=1;
32     queue<int>q;while(!q.empty()) q.pop();
33     trie[1].suf=0;
34     q.push(1);
35     while(!q.empty())
36     {
37         int u=q.front();q.pop();
38         for (int i=0;i<26;i++)
39             if (trie[u].c[i])
40             {
41                 trie[trie[u].c[i]].suf=trie[trie[u].suf].c[i];
42                 if (trie[trie[trie[u].c[i]].suf].flag) trie[trie[u].c[i]].flag=true;
43                 q.push(trie[u].c[i]);
44             }
45             else trie[u].c[i]=trie[trie[u].suf].c[i];
46     }
47 }
48 inline void dp(int x)
49 {
50     for (int i=1;i<=cnt;i++)
51     {
52         if (trie[i].flag||f[x-1][i]==0) continue;
53         for (int j=0;j<26;j++)
54             f[x][trie[i].c[j]]=(f[x][trie[i].c[j]]+f[x-1][i])%mod;
55     }
56 }
57 int main()
58 {
59     scanf("%d%d",&n,&m);
60     for (int i=1;i<=n;i++)
61     {
62         scanf("%s",ch);
63         insert();
64     }
65     make_AC();
66     f[0][1]=1;
67     for (int i=1;i<=m;i++) dp(i);
68     for (int i=1;i<=m;i++)
69         ans=(ans*26)%mod;    
70     for (int i=1;i<=cnt;i++)
71         if (trie[i].flag==0) res=(res+f[m][i])%mod;
72     printf("%d\n",(ans-res+mod)%mod);     
73 }

 

posted @ 2017-10-25 20:54  Kaiser-  阅读(83)  评论(0编辑  收藏