#include<iostream>
#include<cstdio>
const int ASSIZE=26;
struct Trie{
int cnt;
Trie* fail;
Trie* next[ASSIZE];
Trie(){
cnt=0;
fail=NULL;
for(int i=0;i!=ASSIZE;++i) next[i]=NULL;
}
};
const int N=1000006;
char str[1000010], keyword[51];
void insert(Trie *root,const char* s){
if(root==NULL||*s=='\0') return;
while(*s!='\0'){
if(root->next[*s-'a']==NULL){
root->next[*s-'a']=new Trie;
}
root=root->next[*s-'a'];
++s;
}
++(root->cnt);
return;
}
Trie *q[N];
void build_AC_automachine(Trie* root){ //确定fail 过程是复杂的!
int head=0,tail=0;
q[tail++]=root;
while(head!=tail){
Trie* p=q[head++];
for(int i=0;i!=ASSIZE;++i){ //参见上面网址,说得好
if(p->next[i]==NULL) continue;
if(p==root) p->next[i]->fail=root;
else{
Trie* tmp=p->fail;
while(tmp!=NULL){
if(tmp->next[i]!=NULL){
p->next[i]->fail=tmp->next[i];
break;
}
tmp=tmp->fail;
}
if(tmp==NULL) p->next[i]->fail=root;
}
q[tail++] = p->next[i]; //入队
}
}
}
int query(Trie* root,const char* s){
int r=0;
Trie* p=root;
for( ;*s!='\0';++s){
while(p->next[*s-'a']==NULL&&p!=root) p=p->fail;
p=p->next[*s-'a'];
if(p==NULL) p=root;
Trie* tmp=p;
while(tmp!=root&&tmp->cnt!=-1){
r+=tmp->cnt;
tmp->cnt=-1;
tmp=tmp->fail;
}
}
return r;
}
void del(Trie *root){ //内存释放
for(int i=0;i!=ASSIZE;++i){
if(root->next[i]!=NULL)
del(root->next[i]);
}
delete root;
return;
}
int main()
{
// freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
int ncase, num;
scanf("%d", &ncase);
while(ncase--)
{
Trie* root = new Trie();
scanf("%d", &num);
getchar();
for(int i = 0; i < num; ++i)
{
gets(keyword);
insert(root,keyword);
}
build_AC_automachine(root);
scanf("%s", str);
printf("%d\n", query(root,str));
del(root);
}
return 0;
}
//http://lzw.me/Category/security/1274.html?replytocom=1627
代码基本上来自上面链接