AC自动机学习小结
Posted jklover
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了AC自动机学习小结相关的知识,希望对你有一定的参考价值。
AC自动机
简要说明
- (AC) 自动机,全称 (Aho-Corasick automaton) ,是一种有限状态自动机,应用于多模式串匹配.在 (OI) 中通常搭配 (dp) 食用.因为它是状态自动机.
- 感性理解:在 (Trie) 树上加上 (fail) 指针.具体的讲解可以去看dalao们的博客
(因为我实在是太菜了讲不好).
题目
Keywords Search
- 题目:给若干个模式串,再给一个文本串,问有几个模式串在文本串中出现过.
- 板子题.注意一个模式串只被计算一次,统计后做上标记.
- 这里采用的是补全 (Trie) 树的写法.
View code
#include"bits/stdc++.h" using namespace std; typedef long long LoveLive; inline int read() { int out=0,fh=1; char jp=getchar(); while ((jp>‘9‘||jp<‘0‘)&&jp!=‘-‘) jp=getchar(); if (jp==‘-‘) { fh=-1; jp=getchar(); } while (jp>=‘0‘&&jp<=‘9‘) { out=out*10+jp-‘0‘; jp=getchar(); } return out*fh; } const int MAXN=5e5+10; const int Siz=26; struct AhoCorasick{ int idx; int ch[MAXN][Siz]; int fail[MAXN]; int val[MAXN]; void init() { idx=0; memset(val,0,sizeof val); memset(ch,0,sizeof ch); memset(fail,0,sizeof fail); } AhoCorasick() { init(); } void ins(char s[],int n) { int u=0; for(int i=0;i
q; for(int i=0;i 玄武密码
- 题意:给若干模式串和一个文本串.求每个模式串在文本串上能匹配的最大前缀长度.
- 将模式串建成一个 (AC) 自动机,匹配文本串的时候往前暴力跳,跳到第一个合法的位置即可.
View code
#include"bits/stdc++.h" using namespace std; typedef long long LoveLive; inline int read() { int out=0,fh=1; char jp=getchar(); while ((jp>‘9‘||jp<‘0‘)&&jp!=‘-‘) jp=getchar(); if (jp==‘-‘) { fh=-1; jp=getchar(); } while (jp>=‘0‘&&jp<=‘9‘) { out=out*10+jp-‘0‘; jp=getchar(); } return out*fh; } const int MAXN=1e7+10; const int Siz=4; int n,m; int len[MAXN]; struct AhoCorasick{ int idx; inline int id(char x) { if(x==‘E‘) return 0; if(x==‘W‘) return 1; if(x==‘N‘) return 2; return 3; } int ch[MAXN][Siz]; int fail[MAXN]; int marked[MAXN]; int f[MAXN]; int val[MAXN]; AhoCorasick() { idx=0; memset(fail,0,sizeof fail); memset(ch,0,sizeof ch); memset(marked,0,sizeof marked); memset(val,0,sizeof val); } void ins(char s[],int v) { int u=0; for(int i=0;i
q; for(int i=0;i Censoring
- 题意:给若干模式串和一个文本串.每次从文本串开头找到一个模式串,将其删去,直到无法删去为止,求出最后剩余的文本.保证任一个模式串中没有其他模式串.
- 将模式串建成一个 (AC) 自动机,用一个栈来维护当前剩余的字符.匹配成功时直接修改栈顶.同时需要维护每个节点在 (Trie) 树上的位置.这样删除后可以立即得出当前的位置 (u) .
View code
#include"bits/stdc++.h" using namespace std; typedef long long LoveLive; inline int read() { int out=0,fh=1; char jp=getchar(); while ((jp>‘9‘||jp<‘0‘)&&jp!=‘-‘) jp=getchar(); if (jp==‘-‘) { fh=-1; jp=getchar(); } while (jp>=‘0‘&&jp<=‘9‘) { out=out*10+jp-‘0‘; jp=getchar(); } return out*fh; } const int MAXN=1e5+10; const int Siz=26; int len[MAXN]; struct AhoCorasick{ int idx; int ch[MAXN][Siz]; int fail[MAXN]; int val[MAXN]; AhoCorasick() { idx=0; memset(ch,0,sizeof ch); memset(fail,0,sizeof fail); memset(val,0,sizeof val); } void ins(char *s,int n,int v) { int u=0; for(int i=0;i
q; for(int i=0;i 单词
- 题意:给出一个由若干单词组成的单词表,问每个单词在这个表中出现了几次.
- 很像一个 (kmp) 或是 (AC) 自动机裸题,然而并没有那么简单.用自动机做 (n) 次匹配,可能会被卡掉.如果一直跳 (fail) 指针,就可以构造一组数据让你一直跳.
- 正确的做法是使用 (fail) 树,连出这样所有的有向边 (fail[x]->x) .自动机上,每个节点都代表了一个前缀,连出边后,可以发现父亲节点是儿子节点的后缀.
- 而一个字符串被匹配的次数恰好等于以它为后缀的前缀数目,即 (fail) 树中子树的大小.
- 插入字符串的时候将经过的每个节点的权值 (+1) ,最后 (dfs) 统计即可.
View code
#include"bits/stdc++.h" using namespace std; typedef long long LoveLive; inline int read() { int out=0,fh=1; char jp=getchar(); while ((jp>‘9‘||jp<‘0‘)&&jp!=‘-‘) jp=getchar(); if (jp==‘-‘) { fh=-1; jp=getchar(); } while (jp>=‘0‘&&jp<=‘9‘) { out=out*10+jp-‘0‘; jp=getchar(); } return out*fh; } const int MAXN=1e6+10; const int Siz=26; int n; struct AhoCorasick{ int idx; int ch[MAXN][Siz]; int fail[MAXN]; int pos[MAXN]; int val[MAXN]; int ecnt; int head[MAXN],to[MAXN],nx[MAXN],siz[MAXN]; AhoCorasick() { idx=0; ecnt=0; memset(ch,0,sizeof ch); memset(fail,0,sizeof fail); memset(val,0,sizeof val); memset(head,0,sizeof head); } void ins(char *s,int len,int v) { int u=0; for(int i=0;i
q; for(int i=0;i 病毒
- 题意:给出若干个 (01) 串,问是否存在一个无限长的 (01) 串,满足所有给出的串都不是它的子串.
- 将给出的串插入到 (AC) 自动机里,那么若存在一个环,环上的节点及它们沿 (fail) 指针向上跳都不经过单词末节点,则符合要求.
- 插入的时候将权值一起合并,最后做一次 (dfs) 即可.
View code
#include"bits/stdc++.h" using namespace std; typedef long long LoveLive; inline int read() { int out=0,fh=1; char jp=getchar(); while ((jp>‘9‘||jp<‘0‘)&&jp!=‘-‘) jp=getchar(); if (jp==‘-‘) { fh=-1; jp=getchar(); } while (jp>=‘0‘&&jp<=‘9‘) { out=out*10+jp-‘0‘; jp=getchar(); } return out*fh; } const int MAXN=3e4+10; const int Siz=2; struct AhoCorasick{ int idx; int ch[MAXN][Siz]; int fail[MAXN]; int val[MAXN]; AhoCorasick() { idx=0; memset(ch,0,sizeof ch); memset(val,0,sizeof val); memset(fail,0,sizeof fail); } void ins(char *s,int len) { int u=0; for(int i=0;i
q; for(int i=0;i 文本生成器
- 题意:给出若干个由大写字母构成的单词,问长度为 (m) ,由大写字母构成的字符串中,包含至少一个单词的数目.对 (10007) 取模.
- 可以先求出不包含任意一个单词的字符串数目,再用总数目(26^m)减去.
- 将单词建成一个 (AC) 自动机,类似上题,合并权值即可求出一个节点是否能被走到.
- 用 (f[i][j]) 表示已经走了 (i) 步,走到了节点 (j) 时的方案数. (O(n^2) dp) 即可.
View code
#include"bits/stdc++.h" using namespace std; typedef long long LoveLive; inline int read() { int out=0,fh=1; char jp=getchar(); while ((jp>‘9‘||jp<‘0‘)&&jp!=‘-‘) jp=getchar(); if (jp==‘-‘) { fh=-1; jp=getchar(); } while (jp>=‘0‘&&jp<=‘9‘) { out=out*10+jp-‘0‘; jp=getchar(); } return out*fh; } const int P=1e4+7; inline int add(int a,int b) { return (a+b) % P; } inline int mul(int a,int b) { return a * b % P; } int fpow(int a,int b) { int res=1; while(b) { if(b&1) res=mul(a,res); a=mul(a,a); b>>=1; } return res; } const int MAXN=7777; const int Siz=26; int n,m; struct AhoCorasick{ int idx; int ch[MAXN][Siz]; int fail[MAXN]; int val[MAXN]; int f[101][MAXN]; AhoCorasick() { idx=0; memset(ch,0,sizeof ch); memset(fail,0,sizeof fail); memset(val,0,sizeof val); memset(f,-1,sizeof f); } void ins(char *s,int len) { int u=0; for(int i=0;i
q; for(int i=0;i 小结
- (AC) 自动机可以解决一部分多模式串有关问题.其附带品 (fail) 树也有不错的性质.
以上是关于AC自动机学习小结的主要内容,如果未能解决你的问题,请参考以下文章