AC自动机学习笔记

原博客地址:https://www.cnblogs.com/hyfhaha/p/10802604.html

问题的背景

来一道题:

给定n个模式串和1个文本串,求有多少个模式串在文本串里出现过。

注意:是出现过,就是出现多次只算一次。

img

我们将n个模式串建成一颗字典树。但是当我们匹配成功一个模式串后,需要重新回到根做匹配,这样效率太低了。

比如这个字典树,我们从4号点匹配失败后,可以继续从7号点开始匹配,然后匹配到8。

那么我们怎么确定从哪个点开始匹配呢?我们称i匹配失败后从j开始匹配,j是i的失配指针。

失配指针的实质含义是什么?

如果一个点的Fail指针指向j,那么root到j的字符串是root到i的字符串的一个后缀。

所以Fail指针的含义是:最长的当前字符串的后缀在字典树上可以找到的末尾编号。

求Fail指针

首先我们可以确定,每一个点i的Fail指针指向的点的深度一定是比i小的。

第一层的Fail一定指的是root。

设点i的父亲fa的Fail指针指的是Fafail,那么如果Fafail有和i值相同的儿子j,那么i的Fail就指向j。(此时我已经懵逼)

具体实现:

void getFail () {
    for (int i=0;i<26;i++) Trie[0].son[i]=1;
    queue<int> q;
    q.push(1);
    Trie[1].fail=0;
    while (!q.empty()) {
        int u=q.front();
        q.pop();
        for (int i=0;i<26;i++) {
            int v=Trie[u].son[i];
            int Fail=Trie[u].fail;
            if (!v) {
                Trie[u].son[i]=Trie[Fail].son[i];
                continue;
            }
            Trie[v].fail=Trie[Fail].son[i];
            q.push(v);
        }
    }
}

 

查询

int query (char * s) {
    int u=1;
    int ans=0;
    int len=strlen(s);
    for (int i=0;i<len;i++) {
        int v=s[i]-'a';
        int k=Trie[u].son[v];
        while (k>1&&Trie[k].f!=-1) {
            ans+=Trie[k].f;
            Trie[k].f=-1;
            k=Trie[k].fail;
        }
        u=Trie[u].son[v];
    }
    return ans;
}

 

完整代码

#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+100;
struct trie {
    int son[26];
    int f;
    int fail;
}Trie[maxn];
int n,cnt;
char s[maxn];
queue<int> q;
void insert (char * s) {
    int u=1;
    int len=strlen(s);
    for (int i=0;i<len;i++) {
        int v=s[i]-'a';
        if (!Trie[u].son[v]) Trie[u].son[v]=++cnt;
        u=Trie[u].son[v];
    }
    Trie[u].f++;
} 
void getFail () {
    for (int i=0;i<26;i++) Trie[0].son[i]=1;
    queue<int> q;
    q.push(1);
    Trie[1].fail=0;
    while (!q.empty()) {
        int u=q.front();
        q.pop();
        for (int i=0;i<26;i++) {
            int v=Trie[u].son[i];
            int Fail=Trie[u].fail;
            if (!v) {
                Trie[u].son[i]=Trie[Fail].son[i];
                continue;
            }
            Trie[v].fail=Trie[Fail].son[i];
            q.push(v);
        }
    }
}
int query (char * s) {
    int u=1;
    int ans=0;
    int len=strlen(s);
    for (int i=0;i<len;i++) {
        int v=s[i]-'a';
        int k=Trie[u].son[v];
        while (k>1&&Trie[k].f!=-1) {
            ans+=Trie[k].f;
            Trie[k].f=-1;
            k=Trie[k].fail;
        }
        u=Trie[u].son[v];
    }
    return ans;
}
int main () {
    cnt=1;
    cin>>n;
    for (int i=1;i<=n;i++) scanf("%s",s),insert(s);
    getFail();
    scanf("%s",s);
    printf("%d\n",query(s));
    return 0;
}

 

posted @ 2020-06-29 19:50  zlc0405  阅读(135)  评论(0编辑  收藏  举报