http://www.cnblogs.com/Booble/archive/2010/12/05/1897121.html
//hdu2222
//ACauto
//構造失敗指針:設當前節點上的字母爲C,沿着他父親的失敗指針走,直到走到一個節點,他的兒子中也有字母爲C的。然後把當前節點的失敗指針指向那個字母也爲C的兒子。如果一直走到了root都沒找到,那就把失敗指針指向root。
//匹配(1)當前字符匹配,只需沿該路徑走向下一個節點繼續匹配即可;(2)當前字符不匹配,則去當前節點失敗指針所指向的字符繼續匹配.重複這2個過程中的一個,直到模式串走完。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<queue>
#define cha 26
#define Root 0
#define N 500001
using namespace std;
struct node{
int data;//結點信息
int count;//從根到此處是否是關鍵字,並且記錄是多少個關鍵字的結尾
int fail;
int next[cha];
}tree[N];
void init(node &a,int data){
a.data = data;
a.count = 0;
a.fail = Root;
for(int i=0;i<cha;i++)
a.next[i] = -1;
}
int k = 1;
void Insert(char s[]){
int p = Root;
for(int i=0;s[i];i++){
int data = s[i]-'a';
if(tree[p].next[data]==-1){//不存在該結點
init(tree[k],data);
tree[p].next[data] = k;
k++;
}
p = tree[p].next[data];
}
tree[p].count++;
}
queue<node> q;
void AC_automation(){
q.push(tree[Root]);
while(!q.empty()){
node k = q.front();
q.pop();
for(int j=0; j<cha; j++){
if( k.next[j]!=-1 ){
if( k.data == -1 ) tree[k.next[j]].fail = Root;
else{
int t = k.fail;
while( t!=Root && tree[t].next[j]==-1 ) t = tree[t].fail;
tree[ k.next[j] ].fail = max( tree[t].next[j], Root );
}
q.push(tree[k.next[j]]);
}
}
}
}
int get_ans(char s[]){
int k=Root, ans = 0;
for(int i=0;s[i];i++){
int t = s[i] - 'a';
while(tree[k].next[t]==-1 && k ) k = tree[k].fail;
k = tree[k].next[t];
if(k==-1){ k = Root;continue;}
int j = k;
while( tree[j].count ){
ans += tree[j].count;
tree[j].count = 0;
j = tree[j].fail;
}
//下面兩句很重要,如果走到頭以後當前字母不是關鍵字終點然而其fail指針指向字母是關鍵字終點的話,
//應當加入此關鍵值,而網上大多數程序忽視了這一點導致hdu的discuss裏反例過不了
ans += tree[ tree[j].fail ].count;
tree[ tree[j].fail ].count = 0;
}
return ans;
}
char tar[2*N];
int main(){
int T,n;
scanf("%d",&T);
while(T--){
scanf("%d",&n);
init(tree[Root],-1);
char a[55];
while(n--){
scanf("%s",a);
Insert(a);
}
AC_automation();
scanf("%s",tar);
printf("%d\n",get_ans(tar));
}
return 0;
}