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
",aho.match(S));
201     }
202  } 
原文地址:https://www.cnblogs.com/Kingpenguin/p/10871446.html