AC自动机构造有两个步骤:
- 基础的Trie结构
- 为Trie上每个结点构造失配指针
失配指针
AC 自动机利用一个 fail 指针来辅助多模式串的匹配。
状态 的 fail 指针指向另一个状态 ,其中 ,且 是 的最长后缀(即在若干个后缀状态中取最长的一个作为 fail 指针)。对于学过 KMP 的朋友,我在这里简单对比一下这里的 fail 指针与 KMP 中的 next 指针:
- 共同点:两者同样是在失配的时候用于跳转的指针。
- 不同点: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;
}
题目