每次第一道模板题都是非常有意义的,考试前夕费尽心思学了KMP,学了Trie树,就是为了学这个做铺垫的,这道题时著名的AC自动机模板题。个人理解AC自动机就是在一棵Trie树上求失配指针,然后实现了多模匹配。只需遍历一次文本串就能求出所有的内容。在下面的query代码里,因为不能重复计算相同的模板串,所以每次加上后temp->count=-1,表示已经算过,在while循环里temp->count!=-1的时候才进行失配其实是有意义的,当temp->count==-1时说明已经沿着失配指针匹配过一次了,所以就没有必要再往上跑一次。假如每次匹配完我令temp->count=0,然后while循环里少了temp->count!=-1的话也是可以过的,前者200ms,后者600ms,就是因为多了不必要的计算。下面贴一记代码
// // main.cpp // AC auto machine // // Created by change on 14-1-24. // Copyright (c) 2014年 chanme. All rights reserved. // #include <iostream> #include <cstring> #include <queue> #include <cstdio> #include <algorithm> #define MAXCHAR 26 #define maxn 500000 using namespace std; struct TrieNode { int count; TrieNode* next[MAXCHAR]; TrieNode* fail; void init() { memset(next,0,sizeof(next)); fail=NULL; count=0; } }T[maxn],*Trie; int top; void insert(char *c) { int len=(int)strlen(c);TrieNode *p=Trie; for(int i=0;i<len;i++){ if(p->next[c[i]-'a']!=NULL){ p=p->next[c[i]-'a']; } else{ T[top].init(); p->next[c[i]-'a']=&T[top++]; p=p->next[c[i]-'a']; } } p->count++; } void getFail() { queue<TrieNode *> que; // 用于BFS的队列 que.push(Trie); // 将根结点压入 Trie->fail=NULL; // 根结点的失配指针为NULL while(!que.empty()) { TrieNode* temp=que.front(); que.pop(); TrieNode* p=NULL; // 枚举每个后继 for(int i=0;i<MAXCHAR;i++){ // 后继非空时计算其失配指针 if(temp->next[i]!=NULL) { // 根结点的所有后继的失配指针指向根结点 if(temp==Trie){ temp->next[i]->fail=Trie; } else{ // 否则的话,沿着失配指针走,遇到有相同后继的则连失配指针 p=temp->fail; while(p!=NULL){ if(p->next[i]!=NULL){ temp->next[i]->fail=p->next[i]; break; } p=p->fail; } // 否则也指向根结点 if(p==NULL) temp->next[i]->fail=Trie; } // 压入队列 que.push(temp->next[i]); } } } } int query(char *c) { int len=(int)strlen(c); int res=0; TrieNode *p=Trie; for(int i=0;i<len;i++){ while(p->next[c[i]-'a']==NULL && p!=Trie) p=p->fail; p=p->next[c[i]-'a']; if(p==NULL) p=Trie; TrieNode *temp=p; while(temp!=Trie&&temp->count!=-1){ res+=temp->count; temp->count=-1; temp=temp->fail; } } return res; } char str[55]; char text[1005000]; int n; int main() { int cas;cin>>cas; while(cas--) { top=0;T[top].init(); Trie=&T[top++]; cin>>n; for(int i=0;i<n;i++){ scanf("%s",str); insert(str); } getFail(); scanf("%s",text); printf("%d ",query(text)); } return 0; }