P3796 【模板】AC自动机(加强版)


在这里插入图片描述


AC自动机板子

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

struct Ans {
    int pos, num;

    bool operator<(Ans &b) const {
        if (num == b.num) {
            return pos < b.pos;
        }
        return num > b.num;
    }

} ans[200];

namespace AC {//AC自动机板子
	const int maxn = 26;

    struct Tree {//字典树
        int fail;//失配指针
        int vis[maxn];//子节点位置
        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 id) {
        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 = id;
        //AC[now].end++;//标记单词结尾
    }

    queue<int> q;

    void get_fail() {//构造fail指针
        for (int i = 0; i < maxn; ++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 < maxn; ++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, res = 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].num++;
				//如果j这个点不是任何文本串的节点 则会在ans[0]上被统计 
				
                //res += AC[j].end;
                //AC[j].end = -1;
            }
        }
        return res;
    }
    
    void init() {
        cnt = 0;
        clear(0);
    }
}
using namespace AC;

string s[200];
//string 可以开到1e6+10 且没有MLE

int main() {
    ios::sync_with_stdio(0);

    while (cin >> n) {
        if (!n) break;
        //init
        init();
        for (int i = 1; i <= n; ++i) {
            cin >> s[i];
            ans[i] = {i, 0};
            build(s[i], i);
        }
        
        get_fail();
        cin >> s[0];
        AC_Query(s[0]);

        sort(ans + 1, ans + n + 1);
        cout << ans[1].num << endl;
        cout << s[ans[1].pos] << endl;

        for (int i = 2; i <= n; ++i) {
            if (ans[i].num == ans[i - 1].num) {
                cout << s[ans[i].pos] << endl;
            } else break;
        }
    }
    return 0;
}

Trie 树

Trie 图

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值