P3808 【模板】AC自动机(简单版)

学习1:小蒟蒻yyb的博客
学习2:大佬的板子
AC自动机:求多个模式串在文本串中出现的次数
在这里插入图片描述

#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
const int INF = 0x3f3f3f3f;
int n, m, k;

namespace AC {//AC自动机板子
    struct Tree {//字典树
        int fail;//失配指针
        int vis[26];//子节点位置
        int end;//标记有几个单词以这个节点
    } AC[N];//trie树 根节点为0

    int cnt = 0;//编号
	
	void clear(int x){//清空
        memset(AC[x].vis, 0, sizeof(AC[x].vis));
        AC[x].fail = 0;//结束标志 一般以0为root
        AC[x].end = 0;
    }

    void build(string s) {
        int len = s.length();
        int now = 0;//字典树当前指针
        for (int i = 0; i < len; ++i) {
            if (AC[now].vis[s[i] - 'a'] == 0) {
                //trie树没有这个子节点
                AC[now].vis[s[i] - 'a'] = ++cnt;
                clear(cnt);
            }
            now = AC[now].vis[s[i] - 'a'];
        }
        AC[now].end++;//标记单词结尾
    }

    queue<int> q;

    void get_fail() {//构造fail指针
        for (int i = 0; i < 26; ++i) {//第二层提前预处理
            if (AC[0].vis[i]) {
                AC[AC[0].vis[i]].fail = 0;//指向根节点
                q.push(AC[0].vis[i]);
            }
        }

        while (!q.empty()) {
            int u = q.front();
            q.pop();

            for (int i = 0; i < 26; ++i) {
                if (AC[u].vis[i]) {//存在子节点
                    AC[AC[u].vis[i]].fail = AC[AC[u].fail].vis[i];
                    //子节点的fail指针 指向父节点fail指针所指向的节点 的相同的子节点 vis[i]相同
                    //如果那个子节点不存在 相当于该子节点fail=0 指向了根节点
                    q.push(AC[u].vis[i]);
                } else {
                    //不存在子节点
                    AC[u].vis[i] = AC[AC[u].fail].vis[i];
                    //当前节点的子节点 指向 当前节点fail指针指向的节点的子节点
                    //把当前节点fail指向的节点 的子节点 作为自己的子节点
                }
            }
        }
    }

    int AC_Query(string s) {//这里的s是被匹配的文本串
        int len = s.length();
        int now = 0, ans = 0;
        for (int i = 0; i < len; ++i) {
            now = AC[now].vis[s[i] - 'a'];
            for (int j = now; j && AC[j].end != -1; j = AC[j].fail) {
                //j不能延伸到根节点 且AC[j]不能是走过的
                ans += AC[j].end;
                AC[j].end = -1;
            }
        }
        return ans;
    }

    void init() {
        cnt = 0;
        clear(0);
    }
}
using namespace AC;


string s;
int main() {
    ios::sync_with_stdio(0);
    cin >> n;
    for (int i = 1; i <= n; ++i) {
        cin >> s;
        build(s);
    }
    get_fail();//求出失配指针
    cin >> s;
    cout << AC_Query(s) << endl;

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值