首页 > 代码库 > 【BZOJ 1030】 [JSOI2007]文本生成器
【BZOJ 1030】 [JSOI2007]文本生成器
1030: [JSOI2007]文本生成器
Time Limit: 1 Sec Memory Limit: 162 MBSubmit: 2017 Solved: 834
[Submit][Status]
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
A
B
Sample Output
AC自动机+dp。
首先把所求转化成 26^m减去一个单词都不包含的方案数。
接下来把单词插到AC自动机上进行dp。
f[i][j]表示走到AC自动机的j号结点当前单词长度为i的方案数。
转移就是枚举下一位的26个字母,看是否可行(如果下一位在j的子节点中出现,若被标记为单词的结尾,就不能转移)
注意对于长度为i的单词,枚举AC自动机上的结点标号j,如果f[i][j]=0,说明长度为i-1的单词没有能转移到j上的,因此f[i][j]自然也不能转移到i+1。
#include <iostream> #include <cstdio> #include <algorithm> #include <cstring> #include <queue> #include <cstdlib> #define mod 10007 using namespace std; char s[100]; queue<int> q; int n,m,tot,ans,f[105][10005]; struct AC { int son[30],ne,last; bool mark; }tree[10005]; void Insert(char str[]) { int l=strlen(str),x=0; for (int i=0;i<l;i++) { int y=str[i]-'A'; if (!tree[x].son[y]) tree[x].son[y]=++tot; x=tree[x].son[y]; } tree[x].mark=true; } void Make_fail() { for (int i=0;i<26;i++) if (tree[0].son[i]) q.push(tree[0].son[i]); while (!q.empty()) { int x=q.front(); q.pop(); for (int i=0;i<26;i++) if (tree[x].son[i]) { int y=tree[x].son[i]; q.push(y); int v=tree[x].ne; while (v&&!tree[v].son[i]) v=tree[v].ne; tree[y].ne=v=tree[v].son[i]; tree[y].last=tree[v].mark?v:tree[v].last; } } } void dp() { f[0][0]=1; for (int i=0;i<m;i++) for (int j=0;j<=tot;j++) if (f[i][j]) for (int k=0;k<26;k++) { int v=j; while (v&&!tree[v].son[k]) v=tree[v].ne; v=tree[v].son[k]; if (!tree[v].mark&&!tree[v].last) f[i+1][v]=(f[i+1][v]+f[i][j])%mod; } for (int i=0;i<=tot;i++) ans=(ans+f[m][i])%mod; } int main() { scanf("%d%d",&n,&m); for (int i=1;i<=n;i++) scanf("%s",s),Insert(s); Make_fail(); dp(); int x=1; for (int i=1;i<=m;i++) x=(x*26)%mod; cout<<(x-ans+mod)%mod<<endl; return 0; }
感悟:
1.注意这道题有一个last标记,因为可能j不是当前字符串的结尾,是别的字符串的结尾
【BZOJ 1030】 [JSOI2007]文本生成器