AC自动机入门题目(HDU - 2222 )+模板+解释

AC自动机其实就是 字典树+KMP。 最开始的时候我没有刷字典树的题目,所以不是很能理解, 但是刷完字典树之后,对AC自动机得理解就比较简单了。
AC自动机的构造中有3个需要注意的地方:
1、next[i][j] 这个next里面存的值和字典树的是几乎一样的(不过也有些不同,下面我会解释的)。存放的是当前所在第i个节点当下一个出现的字符为j的时候的节点位置。
2、fail[i] 这个和kmp中fail是一样的,当前第i个节点失配的话,那就去尝试匹配第fail[i] 里面存放的节点。

**以上就是字典树与kmp的合体,不过不同的是:1、在字典树中如果当前节点为i的话, 如果没有为j的节点, 那么next[i][j] == -1; 但是在AC自动机中里面会存放是,当前i节点的失配指针指向的节点中如果有同样j的节点那么就会next[i][j]指向它,如果其失配指针(fail[i])没有j节点的,那么就找fail[fail[i]] 中的j节点是否存在, 直到一直找到根为止。
2、在构建fail[i]的时候用的是这样的思路(BFS),如果在当前节点失配,那么就去找当前节点的父亲节点的失配指针指向的节点中是否有i节点,如果有就指向它,否则就在去找它的失配节点, 一直到找到或者到根为止。**

3、temp指针,其实在AC自动机里面每次沿着字典树走,每当走到一个新的节点,就会沿着当前节点的失配节点一直往上走,看是否有匹配的节点;
比如 字典树中有这两个字符 acdm, cd.
现在要求匹配的acdmk, 如果没有temp的话就只会找到acdm,但是有了temp指针,就能当走到acdm中的d的时候就可以匹配到cd了。

本人较懒没有附加图片,多多画图,和体会一下,应该就可以懂了的。

#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <cstring>
#include <queue>
#include <cmath>
#include <math.h>
#include <stack>
#include <vector>
using namespace std;
#define Max_N 500010
struct Trie
{
    int next[Max_N][26], fail[Max_N], end1[Max_N];
    int root, L;
    int newnode()
    {
        for (int i = 0; i < 26; i++)
            next[L][i] = -1;
        end1[L++] = 0;
        return L-1;
    }

    void init()
    {
        L = 0;
        root = newnode();
    }

    void insert(char buf[])
    {
        int len = strlen(buf);
        int now = root;
        for (int i = 0; i < len; i++) {
            if (next[now][buf[i] - 'a'] == -1)
                next[now][buf[i]-'a'] = newnode();
            now = next[now][buf[i] - 'a'];
        }
        end1[now]++;
    }

    void build()
    {
        queue<int> q;
        fail[root] = root;
        for (int i = 0; i < 26; i++)
            if (next[root][i] == -1)
                next[root][i] = root;
            else
            {
                fail[next[root][i]] = root;
                q.push(next[root][i]);
            }
        while (!q.empty()) {
            int now = q.front();
            q.pop();
            for (int i = 0; i < 26; 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++) {
            now = next[now][buf[i] - 'a'];
            int temp = now;
            while (temp != root) {
                res += end1[temp];
                end1[temp] = 0;
                temp = fail[temp];
            }
        }
        return res;
    }
};
char buf[1000010];
Trie ac;
int main()
{
    int T;
    int n;
    scanf("%d", &T);
    while (T--) {
        scanf("%d", &n);
        ac.init();
        for (int i = 0; i < n; i++) {
            scanf("%s", buf);
            ac.insert(buf);
        }
        ac.build();
        scanf("%s", buf);
        printf("%d\n", ac.query(buf));
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值