日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

算法学习:AC自动机

發布時間:2025/7/14 编程问答 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 算法学习:AC自动机 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

【定義】

【自動機】??由 狀態集 ,初始狀態集 ,終止狀態集 ,字母集 ,對應關系五個元素組成的結構

     ? 可以簡單的將狀態集理解為結點初始狀態集理解為初始點終止狀態集理解為終點

? ? ? ? ? ? ? ? ? ?字母集理解為一個狀態能夠擁有的出邊的最大個數,而在自動機中,特殊的是,一個結點的所有出邊必須都要存在

? ? ? ? ? ? ? ? ? ?例如在AC自動機中,每個節點都必須要有26個字母的出邊所指向的節點

? ? ? ? ? ? ? ? ? ?對應關系,可以理解為連通的邊,例:節點 u 的 ’a‘ 的出邊能夠到達節點 v ,這就是一組對應關系

?

?注:自動機的概念不用知道也可以學自動機,但是個人感覺理解了自動機的含義,更容易去明白他的本質

? ? ? ? 而且后面學其他自動機也更容易理解

?

【模式串】? 一個比較短的串,需要找? 文本串上有多少個他

?

【文本串】? ?一個比較長的串,需要在? 他上面有多少個模式串

?


【前置知識】

【trie樹(字典樹)】從根節點插入一個字符串,依次插入

【KMP】單文本 logn 復雜度內查找模式串出現次數

?

【強烈建議看最后的擴展】?

?


【解決問題】

?多個模式串匹配文本串

給定一個較長串為文本串,給定多個模式串,詢問這兩者的關系

(也有可能不只有一個文本串)

一般為出現次數什么的

?

AC自動機本身保存這個字符串的所有子串的相關信息

即這個子串作為模式串出現的次數,這個子串的后綴之中能夠包含多少模式串

?


【算法思想】

這個自動機的優點和KMP類似,所以有的博客中也會說,這是一個樹上KMP。

KMP的優點在于有next數組作為指針失配時的指針,使字符串匹配可以不需要再次查找已經查找的串

而AC自動機也有他的“next數組”,f a i l??

AC自動機中的 f a i l 指針表示的是,當當前匹配的模式串失效后,已經匹配的一半模式串的擁有最長后綴的模式串的結尾的位置

?

對于一個AC自動機,我們需要先把所有的模式串都插入一顆 trie 樹

int trie[MAXN][26]; //s是需要插入的模式串 void insert(string s) {int u = 0;//從根節點開始檢索for (int i = 0; i < s.size(); i++){int x = s[i] - 'a';if (!trie[u][x])//如果沒有這個節點//需要新開拓一個節點,保存這個新的分支 {trie[u][x] = ++cnt;}u = trie[u][x];}val[u]++;//說明這個節點是一個模式串的結尾return; }

?

?然后根據我們對 f a i l 的定義去尋找f a i l 指針

?

void get_fail() {queue<int> q;for (int i = 0; i < 26; i++) if (trie[0][i]) fail[trie[0][i]] = 0, q.push(trie[0][i]);//讓所有的根節點連接的節點的fail指針指向根節點//并且加入隊列while (!q.empty()){int u = q.front(); q.pop();for (int i = 0; i < 26; i++)//循環查找每個字母 if (trie[u][i])//如果存在這個節點// {//他的 fail 就是 他父節點的 fail 的這個字母的位置//因為等于是在后綴上新加了一個字母 fail[trie[u][i]] = trie[fail[u]][i];q.push(trie[u][i]);}elsetrie[u][i] = trie[fail[u]][i];//如果沒有//向上遞歸一層,方便之后的查找 }return; }

?

這里應該有張圖說明一下,但是我懶得畫

?


?

?

【模板題】

【題目大意】給n個模式串和1個文本串,問有多少個模式串在文本串中出現過

?

【解決方法】在trie樹上跑文本串

int query(string s) {int u = 0, ans = 0;//從根節點開始依次查找for (int i = 0; i < s.size(); i++){u = trie[u][s[i] - 'a'];//走到自己這個位置字符所在的節點for (int t = u; t && ~val[t]; t = fail[t])//從這個節點開始向上跳fail指針//查找有沒有符合要求的字符串,如果是則這個字符串就會被記錄ans += val[t], val[t] = -1;//-1是為了防止被重復計算 }return ans; }

?

#include<cstdio> #include<iostream> #include<string> #include<queue> using namespace std; const int MAXN = 1000010; int fail[MAXN],cnt; int trie[MAXN][26]; int val[MAXN]; void insert(string s) {int u = 0;for (int i = 0; i < s.size(); i++){int x = s[i] - 'a';if (!trie[u][x]){trie[u][x] = ++cnt;}u = trie[u][x];}val[u]++;return; } void get_fail() {queue<int> q;for (int i = 0; i < 26; i++) if (trie[0][i]) fail[trie[0][i]] = 0, q.push(trie[0][i]);while (!q.empty()){int u = q.front(); q.pop();for (int i = 0; i < 26; i++)if (trie[u][i]){fail[trie[u][i]] = trie[fail[u]][i];q.push(trie[u][i]);}elsetrie[u][i] = trie[fail[u]][i];}return; } int query(string s) {int u = 0, ans = 0;for (int i = 0; i < s.size(); i++){u = trie[u][s[i] - 'a'];for (int t = u; t && ~val[t]; t = fail[t])ans += val[t], val[t] = -1;}return ans; } int main() {int T;cin >> T;while (T--){string s;cin >> s;insert(s);}get_fail();string s;cin >> s;printf("%d", query(s));return 0; } View Code

?


【擴展】

?

這一步擴展其實才是AC自動機的精髓所在,也是比賽中比較常用的方法

就像是網絡流不可能直接給圖找最大流,而是通過建圖的方式考察對算法的理解

AC自動機也有同樣的情況

那就是通過抽離 fail 指針,建 fail 樹,并且對其進行一系列操作完成任務

?

?

這不就是AC自動機抽離fail指針建可持久化線段樹么

?

講解在另外一道題里面

【NOI 2011】 阿貍的打字機

?

轉載于:https://www.cnblogs.com/rentu/p/11297951.html

《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀

總結

以上是生活随笔為你收集整理的算法学习:AC自动机的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。