[BJOI2017]魔法咒语

Description
Chandra 是一个魔法天才。

从一岁时接受火之教会洗礼之后, Chandra 就显示出对火元素无与伦比的亲和力,轻而易举地学会种种晦涩难解的法术。这也多亏 Chandra 有着常人难以企及的语言天赋,让她能轻松流利地说出咒语中那些极其拗口的魔法词汇。

直到十四岁,开始学习威力强大的禁咒法术时, Chandra 才遇到了障碍。

根据火之魔法规则,禁咒的构成单位是 N 个基本词汇。施法时只要凝聚精神力,说出一段用这些词语组成的长度恰好等于 L 的语言,就能释放威力超乎想象的火法术。过去的魔法师们总结了几种表达起来最连贯的组合方式,方便施法者以最快语速完成法术。

但具有魔法和语言双重天才的 Chandra 不满足于这几种流传下来的禁咒,因为她可以毫无困难地说出普通人几乎不可能表达的禁咒语句。然而,在实际施法时, Chandra 发现有些自创禁咒念出后不但没有预期效果,反而会使自己的精神力迅速枯竭,十分难受。

这个问题令 Chandra 万分不解。她大量阅读典籍,到处走访魔法学者,并且不顾精神折磨一次又一次尝试新咒语,希望找出问题的答案。

很多年过去了,在一次远古遗迹探险中, Chandra 意外闯进了火之神艾利克斯的不知名神殿。根据岩土特征分析,神殿应该有上万年的历史,这是极其罕见的。 Chandra 小心翼翼地四处探索,沿着魔力流动来到一间密室。她看见密室中央悬浮着一本书籍。在魔法保护下书籍状况完好。精通上古语言的 Chandra 读过此书,终于解开了多年的困惑。

禁咒法术之所以威力强大,是因为咒语借用了火之神艾利克斯的神力。这本书里记载了艾利克斯生平忌讳的 M 个词语,比如情敌的名字、讨厌的植物等等。使用禁咒法术时,如果语言中含有任何忌讳词语,就会触怒神力而失效,施法者也一并遭受惩罚。

例如,若 ”banana” 是唯一的忌讳词语, “an”、 ”ban”、 ”analysis” 是基本词汇,禁咒长度须是 11, 则“bananalysis” 是无效法术, ”analysisban”、 ”anbanbanban”是两个有效法术。注意:一个基本词汇在禁咒法术中可以出现零次、 一次或多次;只要组成方式不同就认为是不同的禁咒法术,即使书写形式相同。

谜题破解, Chandra 心情大好。她决定计算一共有多少种有效的禁咒法术。

由于答案可能很大,你只需要输出答案模 1,000,000,007的结果。

Input
第一行,三个正整数 N, M, L。
接下来 N 行,每行一个只含小写英文字母的字符串,表示一个基本词汇。
接下来 M 行,每行一个只含小写英文字母的字符串,表示一个忌讳词语。
对于60%的数据1<=N,M<=50,L<=100
对于另40%数据基本词汇长度不超过2,L<=10^8

Output
仅一行,一个整数,表示答案(模 10^9+7)。

Sample Input
4 2 10
boom
oo
ooh
bang
ob
mo

Sample Output
14


BJOI的题感觉就是疯狂分类讨论(一题更比两题强)

首先考虑60%的数据,对禁忌串建立AC自动机,然后设\(f[i][j]\)表示长度为\(i\),匹配到AC自动机的第\(j\)个位置的方案数,转移的时候则需要枚举基本词汇,判断其从\(j\)开始匹配时是否会出现禁忌词汇,否则记匹配完后的位置为\(k\),由\(f[i][j]\)转移到\(f[i+len][k]\)即可

考虑len=1的部分,那么\(f[i][j]\)必然会转移到\(f[i+1][k]\),那么我们就可以用矩阵乘法加速转移,具体而言,如果\(f[i][j]\)可以转移到\(f[i+1][k]\),则转移矩阵\(Tr[j][k]\)加1即可

考虑len=2的部分,类比一下我们求Fibonacci数列时使用的矩阵,同样的,我们记录两个信息,表示\(f[0],f[1]\)(第二维我就不写了),那么转移矩阵就自己画图弄一弄吧

/*program from Wolfycz*/
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define inf 0x7f7f7f7f
using namespace std;
typedef long long ll;
typedef unsigned int ui;
typedef unsigned long long ull;
inline char gc(){
	static char buf[1000000],*p1=buf,*p2=buf;
	return p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++;
}
inline int frd(){
	int x=0,f=1;char ch=gc();
	for (;ch<'0'||ch>'9';ch=gc())	if (ch=='-')	f=-1;
	for (;ch>='0'&&ch<='9';ch=gc())	x=(x<<1)+(x<<3)+ch-'0';
	return x*f;
}
inline int read(){
	int x=0,f=1;char ch=getchar();
	for (;ch<'0'||ch>'9';ch=getchar())	if (ch=='-')	f=-1;
	for (;ch>='0'&&ch<='9';ch=getchar())	x=(x<<1)+(x<<3)+ch-'0';
	return x*f;
}
inline void print(int x){
	if (x<0)	putchar('-'),x=-x;
	if (x>9)	print(x/10);
	putchar(x%10+'0');
}
const int N=1e2,Mod=1e9+7;
int n,m,L;
char S[N+10][N+10];
struct S1{
	int trie[N+10][26],fail[N+10],tot,root;
	bool End[N+10];
	void insert(char *s){
		int len=strlen(s),p=root;
		for (int i=0;i<len;i++){
			if (!trie[p][s[i]-'a'])	trie[p][s[i]-'a']=++tot;
			p=trie[p][s[i]-'a'];
		}
		End[p]=1;
	}
	void make_fail(){
		static int h[N+10];
		int head=1,tail=0;
		for (int i=0;i<26;i++)	if (trie[root][i])	h[++tail]=trie[root][i];
		for (;head<=tail;head++){
			int Now=h[head];
			End[Now]|=End[fail[Now]];
			for (int i=0;i<26;i++){
				if (trie[Now][i]){
					int son=trie[Now][i];
					fail[son]=trie[fail[Now]][i];
					h[++tail]=son;
				}else	trie[Now][i]=trie[fail[Now]][i];
			}
		}
	}
	int check(char *s,int Fir){
		int len=strlen(s),p=Fir;
		for (int i=0;i<len;i++){
			p=trie[p][s[i]-'a'];
			if (End[p])	return -1;
		}
		return p;
	}
}AC;//Aho-Corasick automaton
namespace DP{
	int f[N+10][N+10];
	void main(){
		f[0][0]=1;
		for (int i=0;i<L;i++){
			for (int j=0;j<=AC.tot;j++){
				if (!f[i][j])	continue;
				for (int k=1;k<=n;k++){
					int tmp=AC.check(S[k],j),len=strlen(S[k]);
					if (!~tmp||i+len>L)	continue;
					f[i+len][tmp]=(f[i+len][tmp]+f[i][j])%Mod;
				}
			}
		}
		int Ans=0;
		for (int i=0;i<=AC.tot;i++)	Ans=(Ans+f[L][i])%Mod;
		printf("%d\n",Ans);
	}
};
namespace Matrix{
	int T;
	struct Matrix{
		int v[(N<<1)+10][(N<<1)+10];
		Matrix(){memset(v,0,sizeof(v));}
		void init(){for (int i=0;i<T;i++)	v[i][i]=1;}
	}Trans;
	Matrix operator *(const Matrix &x,const Matrix &y){
		Matrix z;
		for (int i=0;i<T;i++)
			for (int j=0;j<T;j++)
				for (int k=0;k<T;k++)
					z.v[i][k]=(1ll*x.v[i][j]*y.v[j][k]+z.v[i][k])%Mod;
		return z;
	}
	Matrix mlt(Matrix a,int b){
		Matrix res; res.init();
		for (;b;b>>=1,a=a*a)	if (b&1)	res=res*a;
		return res;
	}
	void main(){
		Matrix Ans; Ans.v[0][0]=1; T=(AC.tot<<1)+2;
		for (int i=0;i<=AC.tot;i++)	Trans.v[AC.tot+1+i][i]=1;
		for (int i=0;i<=AC.tot;i++){
			for (int j=1;j<=n;j++){
				int tmp=AC.check(S[j],i),len=strlen(S[j]);
				if (!~tmp)	continue;
				if (len==1){
					Trans.v[i+AC.tot+1][tmp+AC.tot+1]++;
					if (!i)	Ans.v[0][tmp+AC.tot+1]++;
				}else	Trans.v[i][tmp+AC.tot+1]++;
			}
		}
		Ans=Ans*mlt(Trans,L);
		int res=0;
		for (int i=0;i<=AC.tot;i++)	res=(res+Ans.v[0][i])%Mod;
		printf("%d\n",res);
	}
};
int main(){
	n=read(),m=read(),L=read();
	for (int i=1;i<=n;i++)	scanf("%s",S[i]);
	for (int i=1;i<=m;i++){
		static char s[N+10];
		scanf("%s",s);
		AC.insert(s);
	}
	AC.make_fail();
	if (L<=1e2){
		DP::main();
		return 0;
	}
	Matrix::main();
	return 0;
}
posted @ 2019-03-06 21:36  Wolfycz  阅读(736)  评论(0编辑  收藏  举报