1. 程式人生 > >AC自動機學習小結

AC自動機學習小結

|| sizeof line dal code 要求 struct words span

AC自動機

簡要說明

  • \(AC\) 自動機,全稱 \(Aho-Corasick\ automaton\) ,是一種有限狀態自動機,應用於多模式串匹配.在 \(OI\) 中通常搭配 \(dp\) 食用.因為它是狀態自動機.
  • 感性理解:在 \(Trie\) 樹上加上 \(fail\) 指針.具體的講解可以去看dalao們的博客(因為我實在是太菜了講不好).

題目

  • 題目:給若幹個模式串,再給一個文本串,問有幾個模式串在文本串中出現過.
  • 板子題.註意一個模式串只被計算一次,統計後做上標記.
  • 這裏采用的是補全 \(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自動機學習小結