传送门:http://acm.hdu.edu.cn/showproblem.php?pid=2222
#include<algorithm>
#include<iostream>
#include<queue>
using namespace std;
const int maxn=1000000+5;//代表树的大小
const int maxm=26;//代表孩子分支
char a[maxn];
struct Trie
{
int trieN;
int ch[maxn][maxm];
int val[maxn]; //标记已经匹配过的点
int fail[maxn]; //求出根节点到这个节点与根节点到某个点相同的最长前后缀。让它指向最长前缀的后一个节点。
void init()
{
trieN = -1;
newnode();
}
int newnode() //第一层节点fail都指向root=0,而新建一个节点的时候fail被清空为0(所以所有节点的fail都默认为0)
{
memset(ch[++trieN], 0, sizeof(ch[0]));
val[trieN] = fail[trieN] = 0;
return trieN;
}
void insert(const char *str) //
{
int cur = 0;
for (int i = 0;str[i];i++)
{
int d = str[i]-'a'; //d = str[i] - 'a';
if (!ch[cur][d])
ch[cur][d] = newnode(); //在节点cur要新建一个孩子节点child
cur = ch[cur][d]; //让cur指向这个孩子节点。
}
val[cur]++; //这个节点作为这个单词最后一个字符,标记一下。
}
void build() //求fail数组。
{
queue<int> q;
for (int i = 0;i < maxm;i++)
{
if (ch[0][i]) //让第一层节点的fail都指向root再放进队列里
q.push(ch[0][i]);
}
while (!q.empty())
{
int cur = q.front(); q.pop(); //在这里 0=‘a’,1=‘b’....
for (int i = 0;i < maxm;i++) //假设取的是0层的‘a’,对它的分支做处理。
{
if (ch[cur][i])
{ //如果当前节点cur的孩子节点child存在,那么ch[cur][child]的fail应指向最长相同前后缀的前缀的最后一个节点,相当于建立fail
fail[ch[cur][i]] = ch[fail[cur]][i];
q.push(ch[cur][i]);
}
else//如果当前节点cur的孩子节点child不存在,ch[cur][child]代替fail指针直接指向最长相同前后缀的前缀的最后一个节点相应的孩子节点child
ch[cur][i] = ch[fail[cur]][i];
}
}
}
int query(const char *str)
{
int res = 0, cur = 0; //从头开始找。
for (int i = 0;str[i];i++)
{
int d = str[i]-'a'; //d = str[i] - 'a';
cur = ch[cur][d];
int tmp = cur;
while (tmp)
{
if (val[tmp]==-1)
break;
res += val[tmp];
val[tmp]=0;
val[tmp] = -1; //已经匹配过的要标记一下,往回遍历时val值为-1的,就不再继续了。
tmp = fail[tmp];
}
}
return res;
}
}ac;
char s[50+5];
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
int n;
scanf("%d",&n);
getchar();
ac.init();
for(int i=1;i<=n;i++)
{
scanf("%s",s);
ac.insert(s);
}
ac.build();
scanf("%s",a);
printf("%d\n",ac.query(a));
}
return 0;
}