AC自动机总结(超详细注释)

ps:学习AC自动机的时候必须先点亮KMP字典树的技能点。

步骤:

1.将所有模式串建立一个字典树。

2.对字典树建立失配边。

3.用目标串与字典树进行匹配。

写法:

现在通用的AC自动机写法有两类:1.数组。2.指针。(也有把这两种结合到一起的写法)

指针比较直观,但是做有的题时不好操作;数组相对抽象些,但是适用范围广。我用的是数组的写法,借鉴的是kuangbin大神的板子。

例子:

举个例子,有模式串:she  he  say  shr  her

目标串:yasherhs

现在我们要用这5个模式串建立字典树,并构建失配边。如下图:

代码:(对AC自动机的解释都在代码注释中)

#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <queue>
#include <set>
#include <stdlib.h>
using namespace std;
const int maxn = 500010;
struct Trie{
    //next存放的是字典树,在上图中是实线,next[i][j]中i是父亲节点,j是孩子节点
    //fail存放失配边的信息,在上图中是蓝线
    //end标记的是每个模式串末尾节点,在上图中用红色表示
    int next[maxn][26],fail[maxn],end[maxn];
    int root,L;//root是根节点,L是节点编号
    //newnode()创建一个新的节点,并将它的所有孩子节点初始化为-1,返回这个新建节点的编号
    int newnode(){
        for(int i = 0;i < 26;i++)
            next[L][i] = -1;
        end[L++] = 0;
        return L-1;
    }
    //初始化,给根节点初始化为0号节点
    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'];//切换到下一个节点
        }
        end[now]++;//标记末尾节点
    }
    //建立失配边,用bfs的方式建立
    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;//如果没有前缀,就指向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 += end[temp];
                end[temp] = 0;//这里置零是防止重复计数
                temp = fail[temp];
            }
        }
        return res;//返回匹配成功的个数
    }
    //debug函数,不做多解释了
    void dubug(){
    }
};
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;
}

 

全部评论

相关推荐

牛客5655:其他公司的面试(事)吗
点赞 评论 收藏
分享
codemelo:终面的一般都是很高级别的,肯定难约😋
点赞 评论 收藏
分享
点赞 收藏 评论
分享
牛客网
牛客企业服务