AC自动机

看这数据结构的名字就必须学习一个啊~~

AC自动机就是Trie和kmp的结合。

kmp是查询一个字符串,而自动机用于多个字符串的查询,比如给一篇文章和许多字符串,问有多少字符串出现过等。

由于是一般是模板题,先上板啦,在kuangbin大大那里搬过来的。

是HDU2222 的AC代码

#include <stdio.h>
#include <algorithm>
#include <iostream>
#include <string.h>
#include <queue>
using namespace std;

const int N = 500010;   // 字符串个数*字符串长度
const int A = 26;       // 不同字符个数
const int M = 10000;    // 字符串个数

struct ACAutomata {

    int next[N][A], fail[N], end[N];
    int root, L;
    int newNode()
    {
        for (int i = 0; i < A; ++i) next[L][i] = -1;
        end[L] = 0;
        return L++;
    }
    void init()
    {
        L = 0;
        root = newNode();
    }
    void insert(char buf[])
    {
        int len = strlen(buf);
        int now = root;
        for (int i = 0; i < len; ++i) {
            int ch = buf[i] - 'a';
            if (next[now][ch] == -1) next[now][ch] = newNode();
            now = next[now][ch];
        }
        end[now]++;
    }
    void build()
    {
        queue<int> Q;
        fail[root] = root;
        for (int i = 0; i < A; ++i) {
            if (next[root][i] == -1) {
                next[root][i] = root;
            } else {
                fail[ next[root][i] ] = root;
                Q.push( next[root][i] );
            }
        }
        while (Q.size()) {
            int now = Q.front();
            Q.pop();
            for (int i = 0; i < A; ++i) {
                if (next[now][i] == -1) {
                    next[now][i] = next[ fail[now] ][i];
                } else {
                    fail[ next[now][i] ] = next[ fail[now] ][i];
                    Q.push(next[now][i]);
                }
            }
        }
    }
    int query(char buf[])
    {
        int len = strlen(buf);
        int now = root;
        int res = 0;
        for (int i = 0; i < len; ++i) {
            int ch = buf[i] - 'a';
            now = next[now][ch];
            int tmp = now;
            while (tmp != root) {
                res += end[tmp];
                end[tmp] = 0;
                tmp = fail[tmp];
            }
        }
        return res;
    }

} ac;


char buf[1000010];
int main()
{
    int T, n;
    scanf("%d", &T);
    while (T--) {
        scanf("%d", &n);
        ac.init();
        while (n--) {
            scanf("%s", buf);
            ac.insert(buf);
        }
        scanf("%s", buf);
        ac.build();
        printf("%d
", ac.query(buf));
    }
    return 0;
}

L就是AC自动机的状态数。

以bfs的顺序遍历trie。

fail适配指针,fail[i]指第i个状态的下一个失配后转移的位置。没有可以转移的位置就为root。

end[i]表示以状态i为结尾的字符串个数。

query()里面的while用来求重复的字符串,例如she包含he。

类似模板题HDU2896 和 HDU3065 因为没有相等的字符串,end[i]来记录以i结束的字符串id就可以了。

原文地址:https://www.cnblogs.com/wenruo/p/5616210.html