EVERYTHING HAPPENS FOR THE |

wnsyou

园龄:2年4个月粉丝:19关注:16

2024-11-10 21:51阅读: 18评论: 0推荐: 0

AC 自动机

OI-wiki Link and bilibili Link

AC 自动机,主要用于解决多模式串(即需要求出出现次数等的串)匹配的问题,基于字典树。

大致

将模式串建到字典树上,对每个字典树上的节点求出失配指针,根据失配指针建立失配树,用失配树来维护模式串出现次数。

具体构建

建立字典树

略。

失配 fail 指针

失配指针用于辅助多模式串匹配。

节点 x 的失配指针意思是其对应字符串 s 的最长可匹配(在字典树中能找到)后缀对应字典树的哪个节点。

由于是最长可匹配后缀,那么我们可以发现,只要我们不断枚举 fail[x], fail[fail[x]] ...,就可以把其每个可匹配后缀都枚举出来。

当字典树上一个节点 x 在后接一个字符 c、转移至 y 时(假设已经求出了 x 的 fail 指针),只需要枚举每个 x 的每个可匹配后缀,判断在其后面加上 c 是否有对应的节点即可。

但这样做的复杂度似乎不太对,考虑优化。

路径压缩优化(字典图)

为了避免反复枚举一个转移 acb,可以按照深度去求 fail。枚举 x 和后接的一个字符 y 时,如果 x 没有一个 y 转移,则记录 triex,y=triefailx,y,将路径压缩存储下来,否则就更新 failtriex,y=triefailx,y

由于这个操作改变了字典树的结构,所以也被称为建立字典图。

建立 fail 树

求出 fail 后,一般都需要用 fail 树来辅助维护答案。

顾名思义,就是以 failxx 的父亲搭建出的一棵树,其性质是如果 x 对应字符串被成功匹配了一次,那么其父亲也必然会被成功匹配一次,可以用求子树内权值和的方式来求出现次数。

至此,AC 自动机的前置搭建已经完成,那么就是处理匹配的问题了。

处理匹配

仍然是在字典图上面处理,求出答案后把标记打在失配树上,最后用失配树来处理答案。

假设现在匹配到了 x 这个节点,后接一个字符 y,如果字典图上有这个转移则直接转移过去,否则转移到 triefailx,y 去,每接一个字符都要记得在失配树上对应节点打上一个标记。

最后求答案即可。

Code

#include <iostream>
#include <vector>
#include <algorithm>
#define _1 (__int128)1
using namespace std;
using ll = long long;
void FileIO (const string s) {
freopen((s + ".in").c_str(), "r", stdin);
freopen((s + ".out").c_str(), "w", stdout);
}
const int N = 2e5 + 10;
int n, trie[N][30], ndcnt, fail[N], cnt[N], ans[N];
string s;
vector<int> id[N], g[N];
pair<int, int> dfn[N];
void Insert (string s, int x) {
int now = 0;
for (int i = 0; i < s.size(); i++) {
if (!trie[now][s[i] - 'a']) trie[now][s[i] - 'a'] = ++ndcnt, dfn[ndcnt] = {i, ndcnt};
now = trie[now][s[i] - 'a'];
}
id[now].push_back(x);
}
void Get (int x) {
for (int i : g[x])
Get(i), cnt[x] += cnt[i];
for (int i : id[x])
ans[i] = cnt[x];
}
signed main () {
ios::sync_with_stdio(0), cin.tie(0);
// FileIO("");
cin >> n;
for (int i = 1; i <= n; i++)
cin >> s, Insert(s, i);
cin >> s;
sort(dfn + 1, dfn + ndcnt + 1);
for (int i = 1; i <= ndcnt; i++) {
int x = dfn[i].second;
for (int j = 0; j < 26; j++) {
if (trie[x][j])
fail[trie[x][j]] = trie[fail[x]][j];
else
trie[x][j] = trie[fail[x]][j];
}
}
for (int i = 1; i <= ndcnt; i++)
g[fail[i]].push_back(i);
for (int i = 0, j = 0; i < s.size(); i++) {
if (trie[j][s[i] - 'a']) j = trie[j][s[i] - 'a'];
else j = trie[fail[j]][s[i] - 'a'];
cnt[j]++;
}
Get(0);
for (int i = 1; i <= n; i++)
cout << ans[i] << '\n';
return 0;
}

例题

模板模板+稍微修改,模板还有两个弱化版。

练习题:病毒 | 阿狸的打字机 | 文本生成器

本文作者:wnsyou の blog

本文链接:https://www.cnblogs.com/wnsyou-blog/p/18538588/ac-automaton

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   wnsyou  阅读(18)  评论(0编辑  收藏  举报
  1. 1 勝利への道 安藤浩和
  2. 2 Minecraft’s End Eric Fullerton
  3. 3 月光曲完整版 贝多芬 云熙音乐
  4. 4 平凡之路 (Live版) 朴树
  5. 5 Minecraft C418
  6. 6 Paradise NiziU
  7. 7 叫我,灰原哀 龙大人不喷火
  8. 8 心机之蛙,一直摸你肚子 ——《名侦探柯南》原创同人曲 炊饭,叶辞樱,温海,寒砧,南柯柯,小茜玛姬,盛姝,阿崔Ac,贝壳初,千湛,兮茶子DaYu,乔慕,黎鹿北,起千温卿,遮阳伞,曲悠
  9. 9 战 歌 此去经年
战 歌 - 此去经年
00:00 / 00:00
An audio error has occurred, player will skip forward in 2 seconds.

作词 : 此去经年

作曲 : 此去经年

编曲 : 此去经年

啊啊啊啊啊

啊啊啊啊

啊啊啊

啊啊

加载中…

{{tag.name}}

{{tran.text}}{{tran.sub}}
无对应文字
有可能是
{{input}}
尚未录入,我来提交对应文字
评论
收藏
关注
推荐
深色
回顶
收起
点击右上角即可分享
微信分享提示