AC自動機學習小結
阿新 • • 發佈:2019-01-10
|| sizeof line dal code 要求 struct words span
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自動機學習小結