AC自動機詳解(無指標)

2021-09-02 19:07:09 字數 4416 閱讀 7998

具體來說,就是像搜尋引擎一樣,在搜尋欄裡輸入幾個keyword,然後在大量的網頁文字裡尋找這些關鍵字出現的次數

這不得不令人想起ac自動機的小弟,kmp演算法

kmp實質上是單模匹配,乙個keyword對整個文字的搜尋

也正因為kmp與ac自動機關係像大哥與小弟一樣,掌握ac自動機必須先掌握kmp

在上文中已經說過了,要掌握ac自動機,首先掌握kmp和trie樹(字典樹)

在這裡不展開講解,簡單提幾句即可

kmptrie樹

目的:用樹結構來儲存字串

本質:多叉樹

實現

(從 大佬那裡摳的圖 )

這就是一棵trie樹,儲存了say,****,she,he,her,his幾個單詞

不要在意細節

我們定義乙個空的根節點,給它26個空的子節點分別代表a~z(具體情況具體分析),然後我們將要插入的每乙個字串的首字母以ascll碼代表邊的標號的方式將這個字母壓在邊上,對於每個子節點也進行同樣的操作

具體實現**如下

#define calcid(a) a-97

int tree[maxn][26

];int flag[maxn]

;inline

void

insert

(char

*s)//插入字串s

root=tree[root]

[id]

;//將root指向下一層

} flag[root]++;

}

鳥瞰

思想:在trie樹里跑kmp

核心:fail指標的構建

流程

1.建trie樹

2.在trie樹里構建fail指標

3.將trie樹與主串匹配求解

細節1.trie樹

常規的ac自動機裡的trie樹與一般的trie相比沒有什麼太大的區別,只是要注意樹上節點個數的估計和附加資訊的維護(比如flag)

2.fail指標的構建

這是ac自動機的精華

目的:fail在kmp裡叫做nxt(懂了吧~~)

具體定義:節點u的fail指向v,v是以u代表的字元為結尾的最長當前字串的最長合法真字尾的最後乙個節點(可真繞)

就像上圖

找靠左的節點i的fail指標,

由於以i結尾的當前字串(以第二層的節點開頭,結尾不限)的字尾有shi,hi,i

而shi不是真字尾,hi,i不合法(不合法指沒有與當前字尾完全相同的當前字串),且hi的長度最長,因此滿足要求的最長當前字串的最長合法真字尾是hi,於是找到i的fail指標如下

當然,如果某節點的所有真字尾都非法,那麼就將其指向0號節點即可

於是有下圖

(第二層的s節點和第三層的e節點連向根節點 太懶不想改)

構建:仔細觀察上圖,我們可以發現一些有意思的規律

1.第二層的fail指標的規律

2.沿著fail指標走,深度會越來越小

3.若將fail指標和邊都一視同仁的看作有向邊的話,整個trie樹就成了乙個強聯通分量,可以相互到達

4.如果a節點的fail指向了b節點,那麼a節點的t兒子的fail指向b節點的t兒子(如果兒子不存在,就指根)

這些規律中,第4條對我們構建fail指標有很大幫助

據第四條,我們不難發現,只要確定了第s層的所有點的fail指標,就可以以此來確定第s+1層的所有的點的fail指標

自然而然就想到用乙個bfs來維護fail指標

code:

void

buildfail()

}//更新第二層的fail並開始bfs

while

(!q.

empty()

)else tree[u]

[i]=tree[fail[u]

][i]

;// 如果不存在這個點,我們就將這個空點連向u的fail點的i兒子

}}

3.匹配求解

在構建了trie樹和fail指標後,是時候來求解了

如何求解?

對於未知的主串s來說,它的任何乙個字母都有可能是乙個模式串,因此在求解時很明顯應該乙個字元乙個字元來求解

於是首先來乙個迴圈,對每乙個主串字元,用fail指針對這個字元的所有字首進行一次遍歷查詢,統計出這個字元的答案然後累加即可

#define calcid(a) a-97

intcount

(char

*s)}

return ans;

}

可能以下這段**不是很好理解,因為這是fail指標的精華所在

for

(int t=now;t&&

~flag[t]

;t=fail[t]

)

由於fail指標是指向與該節點表示串字尾相等的且長度最大的串(或字首)的節點

故這個迴圈其實是為了統計當前列舉到的字元是多少個當前字串的字尾

換句話說這個過程就是在遍歷當前構成的字串的字尾中是否有其他的單詞

以上就是ac自動機的基礎內容,下面附上本人奇醜無比的**

luogu模版題

#include

using

namespace std;

//**********************************data

#define calcid(a) a-97

const

int maxn=

1e6+10;

int n,cnt;

int tree[maxn][26

];int flag[maxn]

;int fail[maxn]

;//**********************************function

inline

void

insert

(char

*s)//插入字串s

root=tree[root]

[id]

;//將root指向下一層

} flag[root]++;

}void

buildfail()

}//更新第二層的fail並開始bfs

while

(!q.

empty()

)else tree[u]

[i]=tree[fail[u]

][i]

;// 如果不存在這個點,我們就將這個空點連向u的fail點的i兒子}}

}int

count

(char

*s)}

return ans;

}//**********************************main

intmain()

buildfail()

;scanf

("%s"

,s);

printf

("%d"

,count

(s))

;return0;

}/**

****

****

****

****

****

****

****

****

****

****

****

****

****

****

****

****

**id:andrew_82

lang:c++

prog:aho-corasick automation**

****

****

****

****

****

****

****

****

****

****

****

****

****

****

****

******/

AC自動機詳解

最近真是太頹了,做了一堆板子題,現在對一些知識點順便來個總結記錄 大家應該都知道kmp和trie樹吧,不懂的可以看我部落格或到網上自己動手尋找資料。ac自動機是乙個很好的東西,這是因為它的名字很好它能夠在有多個模式串的時候進行全文匹配,這十分方便地擴充套件了kmp的功能,實際上它的思路與原理與kmp...

AC自動機詳解

首先,ac自動機,不是 自動accepted機,這是乙個多模字串匹配演算法,學習這個演算法,首先要熟悉kmp演算法這個單模字串匹配演算法,然後,我們知道有個高效的多模字串匹配演算法,叫字典樹,它處理的是一些單詞在乙個句子裡出現了幾次,但假如不是在乙個句子裡,而是在乙個字串裡呢?那它就顯得很弱了,所以...

AC自動機詳解

include include include include include include include include using namespace std typedef long long ll const int maxn 2 1e6 9 int trie maxn 26 字典樹 i...