BZOJ1030: [JSOI2007]文本生成器
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
A
B
Sample Output
100
考虑用总方案数-不合法方案数 用f[i][j]表示长度为i位于Tire树中j号节点时所有不合法的方案数。 若当前处于j号节点,且Next[j][k]不是一个字符串的结尾,那么就可以由j号节点向Next[j][k]转移,即f[i][Next[j][k]]=f[i][Next[j][k]]+f[i-1][j]
#include <bits/stdc++.h> #define ll long long using namespace std; const int p=10007; const int N=150; const int M=1e4+50; int n,m; char s[N]; struct acmach { int Next[M][26],Fail[M],End[M],f[N][M]; int root,L; int newnode() { for (int i=0;i<26;i++) Next[L][i]=-1; End[L]=0; return L++; } void init() { L=0; root=newnode(); } void Insert(char s[]) { int len=strlen(s); int now=root; for (int i=0;i<len;i++) { if (Next[now][s[i]-'A']==-1) Next[now][s[i]-'A']=newnode(); now=Next[now][s[i]-'A']; } End[now]++; } void build() { queue<int>q; Fail[root]=root; //当前节点now的失败指针指向的地方 for (int i=0;i<26;i++) if (Next[root][i]==-1) Next[root][i]=root; //下一个字母为i+'a'的节点的下标为Next[now][i] else { Fail[Next[root][i]]=root; q.push(Next[root][i]); } while (!q.empty()) { int now=q.front(); q.pop(); for (int i=0;i<26;i++) if (Next[now][i]==-1) Next[now][i]=Next[Fail[now]][i]; //指向当前节点fail指针的这个子节点 else { Fail[Next[now][i]]=Next[Fail[now]][i]; //这个节点的失败指针指向(((他父亲节点)的失败指针所指向的那个节点)的下一个节点) q.push(Next[now][i]); } End[now]+=End[Fail[now]]; } } int query() { f[0][0]=1; //f[i][j]为当前长度为i的字符串处于Trie树中的第j号结点所具有的方案数。 for (int i=1;i<=m;i++) for (int j=0;j<L;j++) { if (End[j]) continue; for (int k=0;k<26;k++) { if(End[Next[j][k]]) continue; f[i][Next[j][k]]=(f[i][Next[j][k]]+f[i-1][j])%p; } } int ret=1,sum=0; for (int i=1;i<=m;i++) ret=ret*26%p; for (int i=0;i<L;i++) sum=(sum+f[m][i])%p; return (ret-sum+p)%p; } }ac; int main() { ac.init(); scanf("%d%d",&n,&m); for (int i=1;i<=n;i++) { scanf("%s",s); ac.Insert(s); } ac.build(); printf("%d\n",ac.query()); return 0; } /* 2 2 A B */