题目大意:如题 AC自动机模板题一道.
思路:自己根据思想码了一遍,各种调试,然后对照原模板再调,,最后发现...差不多拷贝原模板了,嚓,,,,
其实就是建立trie树,然后用过队列建立失败指针,也是KMP思想,利用前缀...
失败指针尽量找深度深的,就是找最近的(我自己理解最近这个名词..)
最后查询的时候,那么如果有路径就一直下,下不去的时候就找父亲的失败指针,看看这个失败指针有没有含有当前字母的儿子.有就对了,没就一直找到根结点查看根结点的儿子
AC Program:
#include <vector>
#include <list>
#include <map>
#include <set>
#include <queue>
#include <deque>
#include <stack>
#include <bitset>
#include <algorithm>
#include <functional>
#include <numeric>
#include <utility>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <queue>
#include <cassert>
typedef long long ll;
#define clr(a) memset((a),0,sizeof (a))
#define rep(i,a,b) for(int i=(a);i<(int)(b);i++)
#define per(i,a,b) for(int i=((a)-1);i>=(int)(b);i--)
#define inf (0x7fffffff)
#define eps 1e-6
#define MAXN
#define MODN (1000000007)
using namespace std;
int n;
char key[55];
char des[1000005];
struct node{
node * next[26];
node * fail;
int cnt;
node(){
cnt=0;
fail=0;
clr(next);
}
};
//queue<node*>que;
//void init(){
//}
node * insert(node * root ){
//结点指针 不能每次
node * p=root;//工作指针
node * q;
int i=0;
while(key[i]){
int index=key[i]-'a';
//cout<<"index "<<index<<endl;
//cout<<"p->next[index] "<<p->next[index]<<endl;
//还是设置为空的时候申请一个结点,其他的和已经存在的一样,都要移动p和i
if(!p->next[index])
p->next[index]=new node();//指针之后还是指针
p=p->next[index];//p=q;
i++;//遍历串
}
p->cnt+=1;
return root;
}
void ac_auto(node * root){//其实就是建立失败指针
root->fail=0;//根结点的失败指针为0
queue<node*>que;// 弄为全局变量?
//que.clear();
que.push(root);
//工作指针
while(!que.empty()){
node * tmp=que.front();
node * p=0;
que.pop();//记得pop
rep(i,0,26){
//!tmp->next[i]会发生错误
if(tmp->next[i]==0)continue;//如果是没有这个字母就跳出
if(tmp==root){ //如果根结点的子节点特殊情况处理
tmp->next[i]->fail=root;
que.push(tmp->next[i]);
//cout<<"i "<<i<<endl;
continue;
}
//cout<<"i "<<i<<endl;
//node * tmp=p;
p=tmp->fail;
//p=p->fail;//tmp当前的是父亲结点,工作的是儿子结点,为其找失败指针
while(p!=0){//最远的前缀就是在根结点的子结点才找到相应的前缀
if(p->next[i]!=0){
tmp->next[i]->fail=p->next[i];
break;
}//顺着父亲结点的失败指针一直往上找,
//如果不是根结点的话,那么这些结点都是和父亲结点有相同的字符
//知道找到,或者到跟结点为止.(包括根结点还要判断一次)
p=p->fail;
}
if(p==0)//即是找不到这样的前缀,那么失败指针指向root
tmp->next[i]->fail=root;
que.push(tmp->next[i]);//统一上两种情况的入队姿势
//system("pause");
}
}
}
void query(node * root){
node * q=root;
int len=strlen(des);
int index;
int sum=0;//最后的结果
rep(i,0,len){
index=des[i]-'a';
//if(q->next[index]==0)continue;//貌似不能直接跳过,因为还要考虑下q指针
while(q->next[index]==0 && q!=root){
q=q->fail;//如果当前没有这个子结点,那么就根据失败指针倒退,
//知道找到或者回到根结点
}
q=q->next[index];
q=(q==0)?root:q;
node * tmp=q;
while(tmp!=root && tmp->cnt!=-1){
sum+=tmp->cnt;
tmp->cnt=-1;
tmp=tmp->fail;
}
}
cout<<sum<<endl;
}
int main(){
int test;
cin>>test;
while(test--){
scanf("%d",&n);
node * root=new node();
rep(i,0,n){
scanf("%s",key);
insert(root);
}
scanf("%s",des);
ac_auto(root);
query(root);
};
//system("pause");
return 0;
}