学习AC自动机之前,必须先得把字典树和KMP算法给理解透了,否则会让你吃尽苦头,切不可本末倒置。
参考文献: http://www.cs.uku.fi/~kilpelai/BSA05/lectures/slides04.pdf
简单介绍一下AC自动机:
1.必须得先建一颗字典树,这是构建AC自动的基本结构,好比建房子的地基。
2.接下来就是AC自动机的构造了,里面最强大的就是fail指针(失败指针)了,fail指针的作用是当前字符匹配失败的时候字符跳转到下一个和此字符有最长公共前缀的字符,如果没有的话直接跳到根节点,且跳转必是深度深的向深度浅的跳转。 通过深度跳转的规律可知,我们可以利用bfs构建fail指针的状态转移图。
3.最后就是直接通过扫描目标串进行匹配了。
一:AC自动机之多模式串匹配
hdu2222 Keywords Search (入门题)
题目大意:给你n个模式串,然后再给你一个目标串,问你目标串中出现了多少次模式串。
解题思路:O(-1)
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 using namespace std; 6 7 const int maxn=250005; 8 const int maxm=1000005; 9 const int N=26; 10 char str[maxm]; 11 int que[maxn], sz; 12 13 struct node 14 { 15 int fail, next[N]; 16 int count; 17 void init() 18 { 19 fill(next,next+26,0); 20 fail=count=0; 21 } 22 }tree[maxn]; 23 24 void insert(char *s) 25 { 26 int p=0; 27 while(*s) 28 { 29 int c=(*s++)-'a'; 30 if(!tree[p].next[c]) 31 { 32 tree[sz].init(); 33 tree[p].next[c]=sz++; 34 } 35 p=tree[p].next[c]; 36 } 37 tree[p].count++; 38 } 39 40 void Creat_AC() 41 { 42 int head=0, tail=0, now, p, fell; 43 for(int i=0; i<N; i++) 44 if(tree[0].next[i]) 45 { 46 que[tail++]=tree[0].next[i]; 47 tree[tree[0].next[i]].fail=0; 48 } 49 while(head!=tail) 50 { 51 now=que[head++]; 52 fell=tree[now].fail; 53 for(int i=0; i<N; i++) 54 { 55 if(p=tree[now].next[i]) 56 { 57 tree[p].fail=tree[fell].next[i]; 58 que[tail++]=p; 59 } 60 else tree[now].next[i]=tree[fell].next[i]; 61 } 62 } 63 } 64 65 int Search_AC(char *s) 66 { 67 int ans=0, p=0, c; 68 while(*s) 69 { 70 c=(*s++)-'a'; 71 p=tree[p].next[c]; 72 int tmp=p; 73 while(tmp&&tree[tmp].count!=-1) 74 { 75 ans+=tree[tmp].count; 76 tree[tmp].count=-1; 77 tmp=tree[tmp].fail; 78 } 79 } 80 return ans; 81 } 82 83 int main() 84 { 85 int n, T; 86 cin >> T; 87 while(T--) 88 { 89 cin >> n; 90 tree[0].init(); 91 sz=1; 92 for(int i=1; i<=n; i++) scanf("%s",str), insert(str); 93 Creat_AC(); 94 getchar(); 95 gets(str); 96 printf("%d\n",Search_AC(str)); 97 } 98 return 0; 99 }
题目大意:给你n个不同的模式串,然后再给你m个目标串,问你这m个目标串中有多少个目标串含有模式串,含有模式串的目标串排序后输出模式串。
解题思路:AC自动机模板题,但是做的时候还是出了一点小问题,因为这里出现了多个目标串,而我在询问匹配的时候不是进行标记改变了num值的大小,这样就导致了下次另外一个目标串再次询问到这里的时候就出问题了。只有一个目标串时改num值为-1是为了防止再次遍历相同的地方,深刻理解算法才是王道。
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <vector> 5 #include <algorithm> 6 using namespace std; 7 8 const int maxn=100005; 9 const int N=94; 10 char str[maxn]; 11 int que[maxn], sz; 12 int color[maxn]; 13 vector<int>vt; 14 15 struct node 16 { 17 int fail, num; 18 int next[N]; 19 void init() 20 { 21 fill(next,next+N,0); 22 fail=num=0; 23 } 24 } tree[maxn]; 25 26 void insert(char *s, int num) 27 { 28 int p=0; 29 while(*s) 30 { 31 int c=(*s++)-' '; 32 if(!tree[p].next[c]) 33 { 34 tree[sz].init(); 35 tree[p].next[c]=sz++; 36 } 37 p=tree[p].next[c]; 38 } 39 tree[p].num=num; 40 } 41 42 void Creat_AC() 43 { 44 int head=0, tail=0, now, p, fell; 45 for(int i=0; i<N; i++) 46 if(tree[0].next[i]) 47 { 48 tree[tree[0].next[i]].fail=0; 49 que[tail++]=tree[0].next[i]; 50 } 51 while(head!=tail) 52 { 53 now=que[head++]; 54 fell=tree[now].fail; 55 for(int i=0; i<N; i++) 56 { 57 if(p=tree[now].next[i]) 58 { 59 tree[p].fail=tree[fell].next[i]; 60 que[tail++]=p; 61 } 62 else tree[now].next[i]=tree[fell].next[i]; 63 } 64 } 65 } 66 67 bool Search_AC(char *s) 68 { 69 int p=0, c, flag=0; 70 fill(color,color+sz,0); 71 while(*s) 72 { 73 c=(*s++)-' '; 74 p=tree[p].next[c]; 75 int tmp=p; 76 while(tmp&&!color[tmp]&&tree[tmp].num) 77 { 78 flag=1; 79 color[tmp]=1; ///开始没用标记符号,而是直接把tree[tmp].num改成0,其实这样是错误的,因为文本串不止一个,这样改的话就毁坏了所建的AC自动机 80 vt.push_back(tree[tmp].num); 81 tmp=tree[tmp].fail; 82 } 83 } 84 return flag; 85 } 86 87 int main() 88 { 89 int n, m; 90 while(cin >> n) 91 { 92 sz=1, tree[0].init(); 93 for(int i=1; i<=n; i++) scanf("%s",str), insert(str,i); 94 Creat_AC(); 95 cin >> m; 96 int sum=0; 97 for(int i=1; i<=m; i++) 98 { 99 scanf("%s",str); 100 vt.clear(); 101 if(Search_AC(str)) 102 { 103 sum++; 104 printf("web %d:",i); 105 sort(vt.begin(),vt.end()); 106 for(int j=0; j<vt.size(); j++) 107 printf(" %d",vt[j]); 108 puts(""); 109 } 110 } 111 printf("total: %d\n",sum); 112 } 113 return 0; 114 }
题目大意:多个模式串一个目标串,问你模式串在目标串中出现的次数。
解题思路:O(-1)
1 #include <iostream> 2 #include <cstdio> 3 #include <algorithm> 4 #include <cstring> 5 using namespace std; 6 7 const int maxn= 50050; 8 const int maxm= 2000005; 9 const int N= 94; 10 char ss[maxn][55]; 11 char str[maxm]; 12 int que[maxn], sz; 13 int mp[maxn]; 14 15 struct node 16 { 17 int num ,fail; 18 int next[N]; 19 void init() 20 { 21 num=fail=0; 22 fill(next,next+N,0); 23 } 24 }tree[maxn]; 25 26 void insert(char *s, int id) 27 { 28 int p=0; 29 while(*s) 30 { 31 int c=(*s++)-' '; 32 if(!tree[p].next[c]) 33 { 34 tree[sz].init(); 35 tree[p].next[c]=sz++; 36 } 37 p=tree[p].next[c]; 38 } 39 tree[p].num=id; 40 } 41 42 void Creat_AC() 43 { 44 int head=0, tail=0, now, fell, p; 45 for(int i=0; i<N; i++) 46 if(tree[0].next[i]) 47 { 48 que[tail++]=tree[0].next[i]; 49 tree[tree[0].next[i]].fail=0; 50 } 51 while(head!=tail) 52 { 53 now=que[head++]; 54 fell=tree[now].fail; 55 for(int i=0; i<N; i++) 56 { 57 if(p=tree[now].next[i]) 58 { 59 tree[p].fail=tree[fell].next[i]; 60 que[tail++]=p; 61 } 62 else tree[now].next[i]=tree[fell].next[i]; 63 } 64 } 65 } 66 67 void Search_AC(char *s) 68 { 69 fill(mp,mp+sz,0); 70 int p=0; 71 while(*s) 72 { 73 int c=(*s++)-' '; 74 p=tree[p].next[c]; 75 int tmp=p; 76 while(tmp&&tree[tmp].num) 77 { 78 mp[tree[tmp].num]++; 79 tmp=tree[tmp].fail; 80 } 81 } 82 } 83 84 int main() 85 { 86 int n; 87 while(cin >> n) 88 { 89 sz=1, tree[0].init(); 90 for(int i=1; i<=n; i++) scanf("%s",ss[i]), insert(ss[i],i); 91 Creat_AC(); 92 getchar(); 93 gets(str); 94 Search_AC(str); 95 for(int i=1; i<=n; i++) 96 if(mp[i]) printf("%s: %d\n",ss[i],mp[i]); 97 } 98 return 0; 99 }
hdu3695 Computer Virus on Planet Pandora
题目大意:有n个模式串一个两个目标串(两个目标串分别为一个主串以及这个主串的倒置串),问你这两个目标串中出现了多少个不同的模式串。
解题思路:模板题,需要注意的地方是目标串中出现了多少个不同的模式串,不标记的话会重复累加,比如:AABBCC, CCBBAA, 标记了只出现了3个不同的模式串,不标记则有6个,其余的基本直接上模板。
1 #include <iostream> 2 #include <cstdio> 3 #include <algorithm> 4 #include <cstring> 5 using namespace std; 6 7 const int maxn= 250050; 8 const int maxm= 5100005; 9 const int N= 26; 10 char str[maxm]; 11 int que[maxn], sz; 12 int mark[maxn], mp[maxn]; 13 14 struct node 15 { 16 int count ,fail; 17 int next[N]; 18 void init() 19 { 20 count=fail=0; 21 fill(next,next+N,0); 22 } 23 }tree[maxn]; 24 25 void insert(char *s, int id) 26 { 27 int p=0; 28 while(*s) 29 { 30 int c=(*s++)-'A'; 31 if(!tree[p].next[c]) 32 { 33 tree[sz].init(); 34 tree[p].next[c]=sz++; 35 } 36 p=tree[p].next[c]; 37 } 38 tree[p].count=id; 39 } 40 41 void Creat_AC() 42 { 43 int head=0, tail=0, now, fell, p; 44 for(int i=0; i<N; i++) 45 if(tree[0].next[i]) 46 { 47 que[tail++]=tree[0].next[i]; 48 tree[tree[0].next[i]].fail=0; 49 } 50 while(head!=tail) 51 { 52 now=que[head++]; 53 fell=tree[now].fail; 54 for(int i=0; i<N; i++) 55 { 56 if(p=tree[now].next[i]) 57 { 58 tree[p].fail=tree[fell].next[i]; 59 que[tail++]=p; 60 } 61 else tree[now].next[i]=tree[fell].next[i]; 62 } 63 } 64 } 65 66 int Search_AC(string s) 67 { 68 int p=0, i=0, ans=0; 69 fill(mark,mark+sz,0); 70 while(i<s.size()) 71 { 72 int c=s[i]-'A'; 73 p=tree[p].next[c]; 74 int tmp=p; 75 while(tmp&&!mark[tmp]) 76 { 77 mp[tree[tmp].count]=1; 78 mark[tmp]=1; 79 tmp=tree[tmp].fail; 80 } 81 i++; 82 } 83 return ans; 84 } 85 86 int main() 87 { 88 int n, T; 89 cin >> T; 90 while(T--) 91 { 92 cin >> n; 93 sz=1, tree[0].init(); 94 for(int i=1; i<=n; i++) scanf("%s",str), insert(str,i); 95 Creat_AC(); 96 scanf("%s",str); 97 string s=""; 98 int len=strlen(str); 99 int i=0; 100 while(i<len) 101 { 102 if(str[i]=='[') 103 { 104 int tp=0; 105 for(int j=i+1; j<len; j++) 106 if('0'<=str[j]&&str[j]<='9') tp=tp*10+str[j]-'0'; 107 else 108 { 109 while(tp--) s+=str[j]; 110 i=j+2; 111 break; 112 } 113 } 114 else s+=str[i], i++; 115 } 116 int ans=0; 117 fill(mp,mp+n+1,0); ///!!这题要注意的就是两个主串可能有相同的子串(如ABBC BB都正反序列都出现了),不标记的话会重复累加 118 Search_AC(s); 119 reverse(s.begin(),s.end()); 120 Search_AC(s); 121 for(int i=1; i<=n; i++) ans+=mp[i]; 122 cout << ans <<endl; 123 } 124 return 0; 125 }
题目大意:O(-1)。
解题思路 :解码+AC自动机模板
1 #include <iostream> 2 #include <cstdio> 3 #include <algorithm> 4 #include <cstring> 5 using namespace std; 6 7 const int maxn= 50010; 8 const int N= 257; 9 char str[maxn]; 10 int que[maxn], sz; 11 int mark[maxn]; 12 13 char pp[]={"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="}; 14 int fp[N]; 15 char bite[65][7]={ 16 "000000", 17 "000001","000010","000011","000100","000101","000110","000111","001000", 18 "001001","001010","001011","001100","001101","001110","001111","010000", 19 "010001","010010","010011","010100","010101","010110","010111","011000", 20 "011001","011010","011011","011100","011101","011110","011111","100000", 21 "100001","100010","100011","100100","100101","100110","100111","101000", 22 "101001","101010","101011","101100","101101","101110","101111","110000", 23 "110001","110010","110011","110100","110101","110110","110111","111000", 24 "111001","111010","111011","111100","111101","111110","111111"}; 25 26 struct node 27 { 28 int num, fail; 29 int next[N]; 30 void init() 31 { 32 num=fail=0; 33 memset(next,0,sizeof(next)); 34 } 35 }tree[maxn]; 36 37 int change(char *s, int *f) 38 { 39 string ss=""; 40 int len=strlen(s); 41 for(int i=0; i<len; i++) 42 { 43 if(s[i]=='=') break; 44 else ss+=bite[fp[s[i]]]; 45 } 46 int ep=0, m=ss.size()/8; 47 for(int i=0; i<8*m; i+=8) 48 { 49 int tp=1, sum=0; 50 for(int j=i+7; j>=i; j--) 51 sum+=tp*(ss[j]-'0'),tp*=2; 52 f[ep++]=(unsigned char)sum; 53 } 54 f[ep]=0; 55 return ep; 56 } 57 58 void insert(int *s, int len, int id) 59 { 60 int p=0, i=0; 61 while(i<len) 62 { 63 if(!tree[p].next[s[i]]) 64 { 65 tree[sz].init(); 66 tree[p].next[s[i]]=sz++; 67 } 68 p=tree[p].next[s[i]]; 69 i++; 70 } 71 tree[p].num=id; 72 } 73 74 void Creat_AC() 75 { 76 int head=0, tail=0, now, p, fell; 77 for(int i=0; i<N; i++) 78 if(tree[0].next[i]) 79 { 80 que[tail++]=tree[0].next[i]; 81 tree[tree[0].next[i]].fail=0; 82 } 83 while(head!=tail) 84 { 85 now=que[head++]; 86 fell=tree[now].fail; 87 for(int i=0; i<N; i++) 88 { 89 if(p=tree[now].next[i]) 90 { 91 tree[p].fail=tree[fell].next[i]; 92 que[tail++]=p; 93 } 94 else tree[now].next[i]=tree[fell].next[i]; 95 } 96 } 97 } 98 99 int Search_AC(int *s, int len) 100 { 101 fill(mark,mark+sz,0); 102 int p=0, ans=0, i=0; 103 while(i<len) 104 { 105 p=tree[p].next[s[i]]; 106 int tmp=p; 107 // cout << p <<endl; 108 while(tmp&&!mark[tmp]) 109 { 110 mark[tmp]=1; 111 if(tree[tmp].num) ans++; 112 tmp=tree[tmp].fail; 113 } 114 i++; 115 } 116 return ans; 117 118 } 119 120 int main() 121 { 122 int n, m; 123 int tmp[3000]; 124 for(int i=0; i<=64; i++) 125 fp[pp[i]]=i; 126 while(cin >> n) 127 { 128 sz=1, tree[0].init(); 129 for(int i=1; i<=n; i++) 130 { 131 scanf("%s",str); 132 int len=change(str,tmp); 133 insert(tmp,len,i); 134 } 135 Creat_AC(); 136 cin >> m; 137 for(int i=1; i<=m; i++) 138 { 139 scanf("%s",str); 140 int len=change(str,tmp); 141 int ans=Search_AC(tmp,len); 142 printf("%d\n",ans); 143 } 144 puts(""); 145 } 146 return 0; 147 }
二、AC自动机+矩阵(先占坑)
pku 2778 DNA Sequence
HDU2243 考研路茫茫——单词情结
三、AC自动机+DP(先占坑)
pku 3691 DNA repair
HDU 2825 Wireless Password
pku 1625 Censored!