首页 > 代码库 > 【BZOJ 1030】 [JSOI2007]文本生成器

【BZOJ 1030】 [JSOI2007]文本生成器

1030: [JSOI2007]文本生成器

Time Limit: 1 Sec  Memory Limit: 162 MB
Submit: 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

2 2
A
B

Sample Output

100


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]文本生成器