這篇部落格拖了好久好久,真是尷尬,原因大概是我感覺很多東西,都是直覺,有種只可意會不可言傳的味道,想完全搞懂,但後來仔細想想,ac自動機好像本來就不是乙個非常非常確定的演算法,比如說,我該拿覺很多情況可以把演算法卡成n^2。所以拖了很久,醬紫。
先談談ac自動機是什麼。我們知道kmp,可以快速的對乙個字串,用乙個模板進行匹配。然而當我們有多個模板的時候,再去重複的使用kmp演算法就顯得不太合適了。所以我們找到了ac自動機。再簡單的考慮一下,ac自動機可以說,是在一棵tire樹上,掛上kmp的失敗指標即可。
我們給出tire樹部分的**。
void insert(char *s)
}gs[k]++;
}顯然這是非常非常常見的tire樹寫法,關於tire樹,可以參考我以前的部落格。
然後我們給出構造失敗指標的**。
void ac_match()}}
我們回憶一下kmp的演算法,我們在求出第i位的失敗指標的時候,只會用到,i前面的位置。所以同理,我那們在構造失敗指標的時候,我們通過寬度有限搜尋來進行構造,是再合適不過的了。這裡我們明確一下我的陣列的含義。dl,寬搜佇列。sz[i][j]i號結點的下一位j字母的序號。pt[i] i 號結點的失敗指標。我們來詳細的說明pt[i]的含義。pt[i],指我們在i點嘗試尋找他是否有兒子j的時候,如果尋找失敗,我們應該去嘗試pt[i]這個結點的兒子j。如果不理解,可以參考一下部落格
我們給出匹配的**。
void solve(char *s)}}
printf("%d\n",tot);
}這段匹配**是基於下面的hdu模板題而言,是求出,有多少模板串可以和目標串匹配。繼續明確一下陣列含義,gs[i]記錄以i號結點結束的單詞的個數。tot為匹配模板串的數量。mark[i]則保證了,所有可能達到匹配的位置都進行了一次考慮。
最後我覺得我應該說明關於**的一些邊界處理。比如我將0號結點的所有26個字母都設為1,避免了因為完全無法匹配無法停止while的情況。
在這裡推薦一道hdu的ac自動機的模板題,hdu2222
並且在此給出ac**。
#include
#include
#include
#include
#include
using namespace std;
queue dl;
int t,n,tot,sum;
bool mark[1000000];
int sz[1000000][30],gs[1000000],pt[1000000];
char tp[1200000];
void insert(char *s)
}gs[k]++;
}void ac_match()}}
void solve(char *s)}}
printf("%d\n",tot);
}int main()
for (int i = 1;i <= 26;i++) sz[0][i] = 1;
tot = 0,sum = 1;
scanf("%d",&n);
for (int i = 1;i <= n;i++)
ac_match();
scanf("%s",tp);
solve(tp);
}return 0;
}
AC自動機演算法
ac自動機簡介 首先簡要介紹一下ac自動機 aho corasickautomation,該演算法在1975年產生於貝爾實驗室,是著名的多模匹配演算法之一。乙個常見的例子就是給出n個單詞,再給出一段包含m個字元的文章,讓你找出有多少個單詞在文章裡出現過。要搞懂ac自動機,先得有字典樹trie和kmp...
AC自動機演算法
ac自動機簡介 首先簡要介紹一下ac自動機 aho corasick automation,該演算法在1975年產生於貝爾實驗室,是著名的多模匹配演算法之一。乙個常見的例子就是給出n個單詞,再給出一段包含m個字元的文章,讓你找出有多少個單詞在文章裡出現過。要搞懂ac自動機,先得有字典樹trie和km...
AC自動機演算法
簡而言之,就是在kmp演算法的思想上加上trie樹來實現的乙個適合多個模式串的匹配問題。kmp演算法時間複雜度優秀的原因是有乙個next陣列,也叫做失配指標陣列,使得匹配的時間複雜度嚴格o m n o m n o m n ac自動機也有這樣的陣列來在失配的時候去跳到相應應該去的地方。所以,ac自動機...