AC自动机
AC自动机:一个常见的例子就是给出n个单词,再给出一段包含m个字符的文章,让你找出有多少个单词在文章里出现过。
要搞懂AC自动机,先得有模式树(字典树)Trie和KMP模式匹配算法的基础知识。
如果你对KMP算法和了解的话,应该知道KMP算法中的next函数(shift函数或者fail函数)是干什么用的。KMP中我们用两个指针i和j分别表示,A[i-j+ 1..i]与B[1..j]完全相等。也就是说,i是不断增加的,随着i的增加j相应地变化,且j满足以A[i]结尾的长度为j的字符串正好匹配B串的前 j个字符,当A[i+1]≠B[j+1],KMP的策略是调整j的位置(减小j值)使得A[i-j+1..i]与B[1..j]保持匹配且新的B[j+1]恰好与A[i+1]匹配,而next函数恰恰记录了这个j应该调整到的位置。同样AC自动机的失败指针具有同样的功能,也就是说当我们的模式串在Tire上进行匹配时,如果与当前节点的关键字不能继续匹配的时候,就应该去当前节点的失败指针所指向的节点继续进行匹配。
AC自动机算法分为3步:
1.构造一棵Trie树
2.构造失败指针
3.模式匹配过程。
hdu2222,这个题就是AC自动机的裸题,但注意模式可以重复。
代码:
View Code
1 #include <iostream> 2 #include <stdio.h> 3 #include <string> 4 #include <queue> 5 using namespace std; 6 const int maxnum=27; 7 8 struct node 9 { 10 int count; 11 node *next[maxnum]; 12 node *fail; 13 node() 14 { 15 count=0; 16 fail=NULL; 17 int i; 18 for(i=0;i<maxnum;i++) 19 next[i]=NULL; 20 } 21 }; 22 node *root; 23 queue<struct node *> q; 24 25 void Insert(char *str) 26 { 27 node *pp=root; 28 int k; 29 while(*(str)!=0) 30 { 31 k=*str-'a'; 32 if(pp->next[k]==NULL) 33 pp->next[k]=new node(); 34 pp=pp->next[k]; 35 if(*(str+1)==0) 36 pp->count++; //模式可以重复,所以++ 37 str++; 38 } 39 } 40 41 void Delete(node *pp) 42 { 43 int i; 44 for(i=0;i<maxnum;i++) 45 if(pp->next[i]!=NULL) 46 Delete(pp->next[i]); 47 delete pp; //删除pp指针所指的内容,pp地址不变 48 pp=NULL; //pp地址指向0,避免野指针 49 } 50 51 void Traversal(node *pp,string ss) 52 { 53 int i,flag; 54 for(i=0;i<maxnum;i++) 55 { 56 flag=false; 57 if(pp->next[i]!=NULL) 58 { 59 flag=true; 60 string strtemp=ss; 61 ss+=(char)(i+'a'); 62 if(pp->next[i]->count!=0) cout<<ss<<endl; 63 Traversal(pp->next[i],ss); //use p->next[i] instead of p=p->next[i]; 64 ss=strtemp;//ss return to old value 65 } 66 } 67 if(!flag) return ; 68 } 69 70 void Build() 71 { 72 root->fail=NULL; 73 q.push(root); 74 node *temp,*pp; 75 int i; 76 while(!q.empty()) 77 { 78 temp=q.front(); 79 q.pop(); 80 for(i=0;i<maxnum;i++) 81 if(temp->next[i]!=NULL) 82 { 83 if(temp==root) 84 temp->next[i]->fail=root; 85 else 86 { 87 pp=temp->fail; 88 while(pp!=NULL) 89 { 90 if(pp->next[i]!=NULL) 91 { 92 temp->next[i]->fail=pp->next[i]; 93 break; 94 } 95 pp=pp->fail; 96 } 97 if(pp==NULL) 98 temp->next[i]->fail=root; 99 } 100 q.push(temp->next[i]); 101 } 102 } 103 } 104 105 int Query(char *str) 106 { 107 node *pp,*temp; 108 pp=root; 109 int k; 110 int cnt=0; 111 while(*str!=0) 112 { 113 k=*str-'a'; 114 while(pp->next[k]==NULL && pp!=root) 115 pp=pp->fail; 116 pp=pp->next[k]; 117 if(pp==NULL) 118 pp=root; 119 temp=pp; 120 while(temp!=root && temp->count!=-1) 121 { 122 cnt+=temp->count; 123 temp->count=-1; 124 temp=temp->fail; 125 } 126 str++; 127 } 128 return cnt; 129 } 130 131 void check() 132 { 133 node *temp; 134 int i; 135 q.push(root); 136 while(!q.empty()) 137 { 138 temp=q.front(); 139 q.pop(); 140 cout<<temp<<" "<<temp->fail<<endl; 141 for(i=0;i<maxnum;i++) 142 if(temp->next[i]!=NULL) 143 q.push(temp->next[i]); 144 } 145 } 146 147 int main() 148 { 149 char p[55],t[1000007]; 150 int num,i,ans,m; 151 scanf("%d",&m); 152 while(m--) 153 { 154 root=new node(); 155 scanf("%d",&num); 156 for(i=0;i<num;i++) 157 { 158 scanf("%s",p); 159 Insert(p); 160 } 161 scanf("%s",t); 162 Build(); 163 ans=Query(t); 164 cout<<ans<<endl; 165 Delete(root); 166 } 167 168 return 0; 169 } 170 171 /* 172 1 173 5 174 he 175 her 176 say 177 she 178 shr 179 yarhshe 180 */