AC自动机
Posted kingpenguin
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了AC自动机相关的知识,希望对你有一定的参考价值。
1 #include<iostream> 2 #include<algorithm> 3 #include<queue> 4 #include<string.h> 5 #include<stdlib.h> 6 7 using namespace std; 8 9 #define MAX_N 1000006 10 #define MAX_Tot 500005 11 12 struct ACo{ 13 struct state{ 14 //子節點數組 15 int next[26]; 16 //當前節點的失敗指針 17 int fail; 18 //到當前位置的字符串結束個數 19 int cnt; 20 21 }stateTable[MAX_Tot]; 22 23 //當前AC自動機樹的節點個數 24 int size; 25 queue<int> que; 26 //初始化 27 void init() 28 { 29 //將節點初始化 30 while(que.size()) que.pop(); 31 for(int i=0;i<MAX_Tot;i++) 32 { 33 memset(stateTable[i].next,0,sizeof(stateTable[i].next)); 34 stateTable[i].fail = 0; 35 stateTable[i].cnt = 0; 36 } 37 //根節點一定存在,所以節點個數爲1 38 size = 1; 39 } 40 41 //構建字典樹 42 void insert(char *str) 43 { 44 int n = strlen(str); 45 int now = 0; 46 for(int i=0;i<n;i++) 47 { 48 char c = str[i]; 49 //如果到當前節點子節點不存在的話 50 if(!stateTable[now].next[c-‘a‘]) 51 { 52 //開闢新節點,並將節點個數加一,注意size從1開始的 53 stateTable[now].next[c-‘a‘] = size++; 54 } 55 //每次都要進行走到下一節點 56 now = stateTable[now].next[c-‘a‘]; 57 58 } 59 //該字符串便利完之後走到的節點 60 stateTable[now].cnt++; 61 } 62 //構造失配指針 63 void build() 64 { 65 66 //根節點的失配指針設爲-1 67 stateTable[0].fail = -1; 68 //將根節點壓入隊列 69 que.push(0); 70 71 while(que.size()) 72 { 73 //取當前隊列中的第一個,即廣度優先遍歷數,保證每層節點的失敗指針都選擇完成,纔有可能繼續下一層 74 //否則,如果深度優先遍歷會導致指針不爲最優,因爲別的叉沒有被構造。 75 int u = que.front(); 76 //取一個,要將其彈出 77 que.pop(); 78 79 //將當前節點的所有子節點遍歷 80 for(int i=0;i<26;i++) 81 { 82 //如果當前節點的子節點之一存在子節點 83 if(stateTable[u].next[i]) 84 { 85 //判斷當前點是否爲根節點 86 if(u==0) 87 { 88 //如果爲根節點,沒辦法,只能讓當前節點的子節點的失敗指針指向0,即指向根節點 89 //根節點的第一層節點都滿足,可以想一下。 90 stateTable[stateTable[u].next[i]].fail = 0; 91 } 92 //否則 93 else 94 { 95 //記錄當前節點的失敗指針 96 int v = stateTable[u].fail; 97 //如果失敗指針存在的話 98 while(v!=-1) 99 { 100 //並且其失敗指針節點也存在子節點 101 if(stateTable[v].next[i]) 102 { 103 //將當前節點 的 失敗指針 指向其 失敗指針節點 的下一個節點 104 stateTable[stateTable[u].next[i]].fail = stateTable[v].next[i] ; 105 break; 106 } 107 //記錄下失敗指針的位置。 108 v = stateTable[v].fail; 109 } 110 //如果找了半天,其各種祖先節點的失敗指針仍然不存在 111 if(v==-1) 112 { 113 //只能將當前節點的失敗指針指向根節點 114 stateTable[stateTable[u].next[i]].fail = 0; 115 } 116 } 117 //將當前節點入隊列,畢竟要廣搜一層來確定 118 que.push(stateTable[u].next[i]); 119 } 120 } 121 } 122 123 } 124 int Get(int u) 125 { 126 int res = 0; 127 while(u) 128 { 129 //當前節點的不爲根節點 130 //res+上當前節點下的單詞數 131 res = res+stateTable[u].cnt; 132 //當前節點單詞數清零,避免一條子樹下重複加值 133 stateTable[u].cnt = 0; 134 //回溯其失敗指針下滿足條件的單詞數 135 u = stateTable[u].fail; 136 } 137 return res; 138 } 139 //製造匹配函數 140 int match(char *S) 141 { 142 int n = strlen(S); 143 int res = 0,now = 0; 144 for(int i=0;i<n;i++) 145 { 146 char c = S[i]; 147 //存在自不必多說,向下一個指針移動 148 if(stateTable[now].next[c-‘a‘]) 149 { 150 now = stateTable[now].next[c-‘a‘]; 151 } 152 else 153 { 154 //一旦失配,不回溯根節點,而是回溯失敗指針節點 155 int p = stateTable[now].fail; 156 //如果失配指針存在,或者失配指針指向的根節點存在 157 while(p!=-1 && stateTable[p].next[c-‘a‘]==0) 158 { 159 //更新失敗指針的位置,即不=停的向父節點靠攏 160 p = stateTable[p].fail; 161 } 162 //如果只能找到根節點,那就從根節點進行匹配 163 if(p==-1) 164 { 165 now = 0; 166 } 167 else{ 168 //不然,當前節點跳到失敗節點的存在子節點 169 now = stateTable[p].next[c-‘a‘]; 170 } 171 } 172 //如果當前節點下存在的單詞數不爲0 173 if(stateTable[now].cnt) 174 { 175 //記錄到當前節點爲止所有含有的父節點(失敗節點)的單詞數, 176 res +=Get(now); 177 } 178 } 179 return res; 180 } 181 }aho; 182 int T; 183 int n; 184 char S[MAX_N]; 185 186 int main() 187 { 188 scanf("%d",&T); 189 while(T--) 190 { 191 aho.init(); 192 scanf("%d",&n); 193 for(int i=0;i<n;i++) 194 { 195 scanf("%s",S); 196 aho.insert(S); 197 } 198 aho.build(); 199 scanf("%s",S); 200 printf("%d\n",aho.match(S)); 201 } 202 }
以上是关于AC自动机的主要内容,如果未能解决你的问题,请参考以下文章
HDU4057 Rescue the Rabbit(AC自动机+状压DP)
Codeforces 86C Genetic engineering(AC自动机+DP)
POJ1699 Best Sequence(AC自动机+状压DP)