与KMP类似,AC自动机也是用来处理字符串匹配的问题。与KMP不同的是,KMP用来处理单模式串问题,即问模式串T是否是主串S的子串,而AC自动机则能处理多模式串的问题。
AC自动机处理的常见问题如:给出n个单词Ti,再给出一段文章S,问有多少个单词在文章里出现了。
流程
(1)我们将所有的模式串构建成一棵Trie树。
(2)主串与模式串的匹配
- 假设现在匹配到节点u,即我们得到了一个从根到u点组成的字符串,它代表的是模式串中某个串的前缀T[1…j],也是主串的一段子串S[i-j+1…i]。
- 若S[i+1]和T[j+1]能够匹配,即trie上的u点存在字符为S[i+1]的转移边,那么i加一,u变为走转移边到达的下一个节点。
- 当在u匹配失败即Trie上的u点不存在字符为S[i+1]的转移边时,我们需要找到另一个串,使得它有尽量长的前缀与u所代表的串的后缀相等,即T’[1…k] = T[j-k+1…j],其中T’[1…k]也对应着Trie中的某个节点v。此时我们令u等于v,j等于k,继续判断S[i+1]和T’[j+1]是否能够匹配。
(3)建立nxt数组
- nxt数组也就是前缀指针,也就是所有满足T'[1…k] = T[j-k+1…j]的k(k < j)的最大值所对应的节点编号。
- nxt[u]的深度是小于节点u的,因此我们可以按节点的深度大小,也就是BFS的顺序来构建nxt数组。
- 与KMP类似,我们设v,u表示字符串T'[1..j], T[1..i],其中v是u的后缀且nxt[u]=v。枚举u的转移边指向的点x,现在我们要求出nxt[x]。若T'[j+1]=T[i+1],也就是下一位仍然匹配,那么设v的相同字符转移边指向的点为y,令nxt[x]等于y。否则T'[j+1]与T[i+1]失配,我们令v=nxt[v],即跳到字符串v的后缀nxt[v]处,按照以上的过程继续判断是否匹配。若跳到空节点,那么无法匹配,nxt[x]=0。
- 下面,我们用实例描述建立nxt数组(即下述的前缀指针)的过程:



实现
构建AC自动机的Trie与普通Trie无异:
1.void make()
2.{
3. int len = strlen(s), u=1;
4. for (int i=0; i<len; ++i)
5. {
6. int c = s[i]-‘A’;
7. //将’A’~’Z’转为0~25的数字
8. if (!ch[u][c]) ch[u][c] = ++tot; //建立新节点
9. u = ch[u][c];
10. }
11. bo[u] = 1; //有以u结尾的字符串,打上标记
12.}
构建nxt数组:
1.void bfs() //通过bfs构建nxt数组
2.{
3. for (int i=0; i<=25; ++i)
4. ch[0][i] = 1; //为了方便将0的所有转移边都设为根节点1
5. que[1] = 1; nxt[1] = 0; //若在根节点失配,则无法匹配字符
6. for (int q1=1,q2=1; q1<=q2; ++q1)
7. {
8. int u = que[q1];
9. for (int i=0; i<=25; ++i)
10. {
11. if (!ch[u][i]) ch[u][i] = ch[nxt[u]][i]; //此处为优化
12. else {
13. que[++q2] = ch[u][i]; // 若有这个儿子则将其加入队列中
14. int v = nxt[u];
15. while (v > 0 && !ch[v][i]) v = nxt[v];
16. nxt[ch[u][i]] = ch[v][i];
17. }
18. }
19. }
20.}
- 为什么当我们发现不存在u的转移边i时,可以令ch[u][i]等于ch[nxt[u]][i]?
- 其实这是为了优化时间。因为在具体问题中,若不存在ch[u][i]的转移边,往往需要沿u的前缀指针走到第一个满足存在字符i的转移边的点v,得到ch[v][i],那么就可直接将ch[u][i]赋值为ch[v][i]即ch[nxt[u]][i]。
- 也正是这个原因,在构建nxt数组时,我们并没有处理v的转移边i不存在的情况,而是直接令nxt[ch[u][i]] = ch[v][i] (其中ch[v][i]也在之前就处理好了)。
匹配:
1.void find()
2.{
3. int u = 1, k;
4. for (int i = 0; i <= m; ++i)
5. {
6. k = ch[u][a[i]];
7. while (k > 1)
8. {
9. ans += bo[k];
10. bo[k] = 0;
11. k = nxt[k];
12. }
13. u = ch[u][a[i]];
14. }
15. return;
16.}