AC自动机代码

AC自动机构造有两个步骤:

  1. 基础的Trie结构
  2. 为Trie上每个结点构造失配指针

失配指针

AC 自动机利用一个 fail 指针来辅助多模式串的匹配。

状态  的 fail 指针指向另一个状态 ,其中 ,且  是  的最长后缀(即在若干个后缀状态中取最长的一个作为 fail 指针)。对于学过 KMP 的朋友,我在这里简单对比一下这里的 fail 指针与 KMP 中的 next 指针:

  1. 共同点:两者同样是在失配的时候用于跳转的指针。
  2. 不同点:next 指针求的是最长 Border(即最长的相同前后缀),而 fail 指针指向所有模式串的前缀中匹配当前状态的最长后缀。

因为 KMP 只对一个模式串做匹配,而 AC 自动机要对多个模式串做匹配。有可能 fail 指针指向的结点对应着另一个模式串,两者前缀不同。

没看懂上面的对比不要急(也许我的脑回路和泥萌不一样是吧),你只需要知道,AC 自动机的失配指针指向当前状态的最长后缀状态即可。

AC 自动机在做匹配时,同一位上可匹配多个模式串。(cv于AC自动机

一、构造Trie树

void insert(int o, string& t) {
	for (auto& x : t) {
		if (ch[o][x - 'a'] == -1) ch[o][x - 'a'] = ++tot;
		o = ch[o][x - 'a'];
	}
	num[o]++;
}

二、建立失配指针

void bfs() {
	queue<int> q;
	for (int i = 0; i < 26; i++) {
		if (ch[0][i] != -1) q.push(ch[0][i]);
	}
	while (!q.empty()) {
		int no = q.front();
		q.pop();
		for (int i = 0; i < 26; i++) {
			if (ch[no][i] != -1) {
				fail[ch[no][i]] = ch[fail[no]][i];
				q.push(ch[no][i]);
			}
			else ch[no][i] = ch[fail[no]][i];
		}
	}
}

三、查询

int query(string& t) {
	int u = 0, ans = 0;
	for (int i = 0; i < t.size(); i++) {
		u = ch[u][t[i] - 'a'];
		for (int j = u; j != -1 && num[j]; j = fail[j]) {
			if (num[j]) {
				ans += num[j];
				num[j] = 0;
			}
		}
	}
	return ans;
}

AC自动机基础代码

// ConsoleApplication4.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <bits/stdc++.h>
using namespace std;
#define fi first
#define se second
typedef unsigned long ul;
const int N = 1e6 + 5;
int n, m, t;
int ch[N][26];
int fail[N];
int tot = 0;
int num[N];
void insert(int o, string& t) {
	for (auto& x : t) {
		if (ch[o][x - 'a'] == -1) ch[o][x - 'a'] = ++tot;
		o = ch[o][x - 'a'];
	}
	num[o]++;
}
void bfs() {
	queue<int> q;
	for (int i = 0; i < 26; i++) {
		if (ch[0][i] != -1) q.push(ch[0][i]);
	}
	while (!q.empty()) {
		int no = q.front();
		q.pop();
		for (int i = 0; i < 26; i++) {
			if (ch[no][i] != -1) {
				fail[ch[no][i]] = ch[fail[no]][i];
				q.push(ch[no][i]);
			}
			else ch[no][i] = ch[fail[no]][i];
		}
	}
}
int query(string& t) {
	int u = 0, ans = 0;
	for (int i = 0; i < t.size(); i++) {
		u = ch[u][t[i] - 'a'];
		for (int j = u; j != -1 && num[j]; j = fail[j]) {
			if (num[j]) {
				ans += num[j];
				num[j] = 0;
			}
		}
	}
	return ans;
}
void sol() {
	cin >> n;
	for (int i = 0; i < n; i++) {
		string t;
		cin >> t;
		insert(0, t);
	}
	bfs();
	string s;
	cin >> s;
	cout << query(s) << "\n";
}
int main()
{
	memset(ch, -1, sizeof(ch));
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	cout.tie(nullptr);
		sol();
	return 0;
}

题目

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

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

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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值