「學習筆記」AC自動機
阿新 • • 發佈:2021-07-01
還記得2019年暑假,gy在英樂華翻開ybt提高篇的目錄
概述
通常來講,KMP 演算法用來處理單模式串匹配問題。而若要處理多模式串的問題,就要引出 AC自動機 。
AC自動機 是以 Trie 的結構為基礎,結合 KMP 的思想建立的。
步驟
-
將所有模式串構建成一棵 Trie 樹
-
對 Trie 上所有節點構造失配指標(最長字尾)
-
利用失配指標對主串進行匹配。
流程
-
構建指標
記 \(tr[p][c]=v\) 表示結點 \(v\) 的父結點 \(p\) 通過字元 \(c\) 指向 \(v\) 。
記 \(fail[u]\) 表示 \(u\) 的失配指標,即 \(u\) 的最長字尾。
-
若 \(tr[u][i]\) 存在,則 \(fail[tr[u][i]]\) 可以由 \(fail[u]\) 增加一個字元 \(i\) 得到
-
否則,直接將 \(tr[u][i]\) 指向 \(tr[fail[u]][i]\) 的狀態
void build(){ for(int i=0;i<26;i++) if(tr[0][i])q.push(tr[0][i]); while(q.size()){ int u=q.front(); q.pop(); for(int i=0;i<26;i++){ if(tr[u][i]){ fail[tr[u][i]]=tr[fail[u]][i]; q.push(tr[u][i]); } else tr[u][i]=tr[fail[u]][i]; } } }
-
-
匹配函式
以P3808 【模板】AC自動機(簡單版)的要求為例
int query(char *s) { int u=0,res=0; for(int i=1;s[i];i++) { u=tr[u][t[i]-'a']; for(int j=u;j && e[j]!=-1;j=fail[j]){ res+=e[j],e[j]=-1; } } return res; }
板子
#include <bits/stdc++.h> using namespace std; const int N=2e5+5; int head[N],to[N],ne[N],tot; int n,tr[N][26],fail[N],ch,book[N],sz[N]; char s[N*10]; queue<int> q; void build(){ for(int i=0;i<26;i++) if(tr[0][i])q.push(tr[0][i]); while(q.size()){ int u=q.front(); q.pop(); for(int i=0;i<26;i++){ if(tr[u][i]){ fail[tr[u][i]]=tr[fail[u]][i]; q.push(tr[u][i]); } else tr[u][i]=tr[fail[u]][i]; } } } inline void add(int u,int v){ to[++tot]=v; ne[tot]=head[u]; head[u]=tot; } void dfs(int u){ for(int i=head[u];i;i=ne[i]){ int v=to[i]; dfs(v); sz[u]+=sz[v]; } } int main(){ scanf("%d",&n); for(int i=1;i<=n;i++){ scanf("%s",s+1); int u=0; for(int j=1;s[j];j++){ if(!tr[u][s[j]-'a'])tr[u][s[j]-'a']=++ch; u=tr[u][s[j]-'a']; } book[i]=u; } build(); scanf("%s",s+1); for(int u=0,i=1;s[i];i++){ u=tr[u][s[i]-'a']; sz[u]++; } for(int i=1;i<=ch;i++){ add(fail[i],i); } dfs(0); for(int i=1;i<=n;i++)printf("%d\n",sz[book[i]]); return 0; }