/*以hdu 3065为例
ac自动机=kmp+trie
与kmp的思想是一样的,不过实现起来就有相当多的细节问题了。
1.建立字典树是必须的,ch[i][j],i是节点编号,j是儿子。每一个节点都隐含有MAX_SIZE个儿子的指针,0表示该儿子不存在,其他值表示她的位置
2.算出失配边,对于ch[i][j]!=0的处理方法与kmp中是相同的。
但对于ch[i][j]=0的处理有两种,
①不处理,直接跳过,continue
②把ch[i][j],赋值为 ch[Fail[i]][j],这样做的原因是简化代码,提高程序效率。
分析:
在kmp中,有这么一句话while(j>=0&&T[i]!=S[j+1])j=Fail[j];即: 当扫到目标串的i位时,不匹配则沿着失配边一直走下去。
同样 ac自动机中也是这样:当扫到目标串的i位时,不匹配则沿着失配边一直走下去。
②的处理先一步走完了相连的第一个失配边。失配边是按节点的深度递推下来的,
而失配边边必然连向了一个深度较小的节点,较小的点的失配边已经处理,
这样ch[i][j]直接变成了第一个能够与T正在处理的那一位匹配的节点的编号或0(根节点)。
当开始与T匹配时,就不再需要沿失配边走的语句了,直接j=ch[j][i],i是T的那一位的值(说的模糊,看代码就知道了)
3.开始与T匹配了,处理与kmp几乎完全相同,就是2.②的变化。。对于不同的题处理也就不同了,当有多个目标串T对Ac_Trie操作时,一定要保证Ac_Trie不变!!
*/
#include<cstdio>
#include<cstring>
#include<iostream>
#include<queue>
#include<string>
#include<algorithm>
using namespace std;
int const MAX_NODE=1010*55;
int const MAX_SIZE=128;
char mp[1010][55];
int ans[1010];
struct Ac_Trie
{
int ch[MAX_NODE][MAX_SIZE],Fail[MAX_NODE],value[MAX_NODE];
/*
ch[][]用来建字典树,Fail[]表示失配边,value[]存储的是字典树中该节点存储的信息
value[i]=-1,表示从root到编号为i的节点所形成的字符串不存在
ch[i][j],表示编号为i的节点的第j个儿子节点,ch[i][j]=0表示此路径不存在,否则她的值为节点i沿边j走下去的下一个节点编号
其中ch[i][j]=-1,0,可赋予不同的意义,视题意而定
*/
int root,tot;
int newnode()
{
memset(ch[tot],0,sizeof(ch[tot]));
value[tot++]=-1;
return tot-1;
}
void init()
{
tot=0;
root=newnode();
}
void insert(char *s,int key)//构造trie树
{
int len=strlen(s);
int u=root;
for(int i=0;i<len;i++)
{
int v=s[i];
if(!ch[u][v])
ch[u][v]=newnode();
u=ch[u][v];
}
value[u]=key;
}
void build()//构造自动机
{
queue<int>Q;
Fail[root]=root;
for(int i=0;i<MAX_SIZE;i++)
if(ch[root][i])
{
Fail[ch[root][i]]=root;
Q.push(ch[root][i]);
}
while(!Q.empty())
{
int now=Q.front();Q.pop();
for(int i=0;i<MAX_SIZE;i++)
{
if(ch[now][i])
{
Fail[ch[now][i]]=ch[Fail[now]][i];
Q.push(ch[now][i]);
}
else ch[now][i]=ch[Fail[now]][i];
}
}
}
void Ac(char *T)//与目标串匹配
{
int len=strlen(T);
int u=root;
for(int i=0;i<len;i++)
{
int v=T[i];
u=ch[u][v];
int temp=u;
while(temp)
{
if(value[temp]!=-1)
ans[value[temp]]++;
temp=Fail[temp];
}
}
}
};
char T[2000010];
Ac_Trie Ac;
int main()
{
int n,m;
while(scanf("%d",&n)!=EOF)
{
Ac.init();
for(int i=1;i<=n;i++)
{
scanf("%s",mp[i]);
Ac.insert(mp[i],i);
}
Ac.build();
scanf("%s",T);
memset(ans,0,sizeof(ans));
Ac.Ac(T);
for(int i=1;i<=n;i++)
{
// cout<<ans[i]<<endl;
if(ans[i])
printf("%s: %d\n",mp[i],ans[i]);
}
}
return 0;
}