题目大意:如题 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; }