#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
struct node
{
int fail;
int next[26];
int c;
void newnode(){memset(next,-1,sizeof(next));fail=-1,c=0;}
}a[2000000];
int num;
char s[1000005];
void insert()
{
int pos=0,t,i;
for(i=0;s[i];++i)
{
t=s[i]-'a';
if(a[pos].next[t]==-1)
{
num++;
a[num].newnode();
a[pos].next[t]=num;
}
pos=a[pos].next[t];
}
a[pos].c++;
}
void KMP() //bfs实现
{
queue<int> q;
a[0].fail=-1;
q.push(0);
while(!q.empty())
{
int t=q.front();
q.pop();
for(int i=0;i<26;++i) //寻找子节点
{
int x=a[t].next[i];
if(x!=-1) //子节点存在
{
if(t==0)a[x].fail=0; //第一层直接指向根节点
else
{
int p=a[t].fail;//父节点失败指针
while(p!=-1)
{
if(a[p].next[i]!=-1) //指针指向的子节点与当前相同
{
a[x].fail=a[p].next[i];
break;
}
p=a[p].fail; //往上寻找
}
if(p==-1)a[x].fail=0; //父节点失败指针不存在,指向根节点
}
q.push(x);
}
}
}
}
int find()
{
int pos=0,i=0,t,m=0;
while(s[i])
{
t=s[i]-'a';
while(pos!=0 && a[pos].next[t]==-1)pos=a[pos].fail; //沿失败指针往回走
pos=a[pos].next[t];
if(pos==-1)pos=0; //回到根节点
int tpos=pos;
while(tpos!=0 && a[tpos].c!=-1) //寻找过某个标记点后标记为-1
{
m+=a[tpos].c;
a[tpos].c=-1;
tpos=a[tpos].fail;
}
++i;
}
return m;
}
int main()
{
freopen("h.in","r",stdin);
freopen("h.out","w",stdout);
int T,n;
scanf("%d",&T);
while(T--)
{
num=0;
a[0].newnode();
scanf("%d\n",&n);
for(int i=1;i<=n;++i)
{
gets(s);
insert();
}
gets(s);
KMP();
printf("%d\n",find());
}
return 0;
}
失败指针:
对于每个节点,我们可以这样处理:设这个节点上的字母为C,沿着他父亲的失败指针走,直到走到一个节点,他的儿子中也有字母为C的节点。然后把当前节点的失败指针指向那个字目也为C的儿子。如果一直走到了root都没找到,那就把失败指针指向root