Description
给定k个字符串以及长度为n的母串可选字母的集合,问母串要完整出现给定的k个字符串的方案数,答案模1000000007,字符仅包含小写字母。
Input
第一行两个整数n、k,表示字符串的长度和给定字符串的个数。
接下来k行每行一个字符串。
接下来一行1个整数m表示可选字母集合内元素个数。
接下来一行给出一个长为m的字符串,表示字母的集合(可能有重复)。
Output
一个整数ans,表示方案数。
Sample Input
3 2
cr
rh
4
acrh
Sample Output
1
【样例解释】
只有crh符合。
Data Constraint
30%的数据n<=10,m<=3。
60%的数据n<=40。
另有10%的数据k=0。
另有10%的数据m=1。
100%的数据n<=100,m<=10,k<=8,给定字符串长度<=30。
想法:
建一棵trie,然后进行AC自动机
字典树中,fail指针表示fail指针到根的字符串,是这个点到根的字符串的后缀,
这可以通过每个点的父亲的fail指针求得
然后预处理出son[i][j]表示第i个点,当下一个与它连接的字符为j是,j的位置
还有str[i]表示到i这个点是,i到根的字符串包括了哪些字符串的状态
比如说abcd包括了bc,
这样就可以dp了,设f[i][j][s]表示构造到第i位,第i位在trie上的位置为j,所选字符串状态为s的方案数
Code
#include <cstdio>
#include <cstring>
#include <iostream>
#define ll long long
using namespace std;
const ll maxN=101,maxM=11,maxK=9,maxS=256,maxn=1000000007;
char s[9][31],s1[11],ch;
ll tail[9],n,k,m,i,f[110][250][256],cnt,tot,
last[250],next[250],tov[250],j,len,l,y,q,
to[250][27],s2,s3,ans,ch1,fail[250],fa[250],str[250];
struct zhj{
char x;
};
zhj a[250];
void insert(ll x,ll y){
tov[++tot]=y,next[tot]=last[x],last[x]=tot;
}
int main(){
//freopen("a.in","r",stdin);
scanf("%lld%lld",&n,&q);
for (i=1;i<=q;i++){
scanf("%s",s[i]+1);
k=0;
len=strlen(s[i]+1);
for (j=1;j<=len;j++){
for (l=last[k];l>0;l=next[l]){
y=tov[l];
if (a[y].x==s[i][j]){
break;
}
}
if (l>0){
k=y;
}else{
cnt++,a[cnt].x=s[i][j],insert(k,cnt),fa[cnt]=k, k=cnt;
}
}
str[k]+=1<<i-1;
scanf("\n");
}
scanf("%d",&m);
scanf("%s",s1+1);
for (i=1;i<=cnt;i++){
j=fa[i];
while (j!=0){
j=fail[j];
for (k=last[j];k>0;k=next[k]){
y=tov[k];
if (a[y].x==a[i].x){
fail[i]=y;
break;
}
}
if (fail[i]>0) break;
}
to[fa[i]][a[i].x-'a'+1]=i;
}
for (i=0;i<=cnt;i++){
str[i]=str[fail[i]]|str[i];
}
for (i=1;i<=cnt;i++){
for (ch=1;ch<=26;ch++){
if (to[i][ch]>0) continue;
j=i;
while (j!=0){
for (k=last[j];k>0;k=next[k]){
y=tov[k],l=a[y].x-'a'+1;
if (l==ch){
to[i][ch]=y;
break;
}
}
if (to[i][ch]>0) break;
j=fail[j];
}
if (to[i][ch]>0) continue;
for (k=last[0];k>0;k=next[k]){
y=tov[k],l=a[y].x-'a'+1;
if (l==ch){
to[i][ch]=y;
break;
}
}
}
}
memset(f,0,sizeof(f));
f[0][0][0]=1;
for (i=0;i<=n-1;i++){
for (j=0;j<=cnt;j++){
for (s2=0;s2<=(1<<q)-1;s2++){
if (f[i][j][s2]==0) continue;
for (ch1=1;ch1<=m;ch1++){
ch=s1[ch1]-'a'+1,s3=s2,y=to[j][ch];
f[i+1][y][s3|str[y]]=(f[i+1][y][s3|str[y]]+f[i][j][s2])%maxn;
}
}
}
ch=1;
}
ans=0;
for (i=0;i<=cnt;i++){
ans=(ans+f[n][i][(1<<q)-1])%maxn;
}
printf("%lld",ans);
}