1.將所有模式串構建成 trie 樹
2.對 trie 上所有節點構建字首指標(類似kmp中的next陣列)
3.利用字首指針對主串進行匹配
字典樹的構建過程是這樣的,當要插入許多單詞的時候,我們要從前往後遍歷整個字串,
當我們發現當前要插入的字元其節點再先前已經建成,我們直接去考慮下乙個字元即可,
當我們發現當前要插入的字元沒有再其前乙個字元所形成的樹下沒有自己的節點,
我們就要建立乙個新節點來表示這個字元,接下往下遍歷其他的字元。然後重複上述操作。
假設我們有下面的單詞,she , he ,say, her, shr ,我們要構建一棵字典樹
在kmp演算法中,當我們比較到乙個字元發現失配的時候我們會通過next陣列,
找到下乙個開始匹配的位置,然後進行字串匹配,當然kmp演算法試用於單模式匹配,
所謂單模式匹配,就是給出乙個模式串,給出乙個文字串,然後看模式串在文字串中是否存在。
ac自動機中,有fail指標,當發現失配的字元失配的時候,跳轉到fail指標指向的位置,
然後再次進行匹配操作,ac自動機之所以能實現多模式匹配,就歸功於fail指標的建立。
當前節點t有fail指標,其fail指標所指向的節點和t所代表的字元是相同的。
因為t匹配成功後,我們需要去匹配 t->child,發現失配,那麼就從t->fail開始再次匹配。
fail指標的求法:
fail指標用bfs來求得,對於直接與根節點相連的節點來說,
如果這些節點失配,他們的fail指標直接指向root即可,其他節點其fail指標求法如下:
假設當前節點為father,其孩子節點記為child。
求child的fail指標時,首先我們要找到其father的fail指標所指向的節點,
假如是t的話,我們就要看t的孩子中有沒有和child節點所表示的字母相同的節點,
如果有的話,這個節點就是child的fail指標,
如果發現沒有,則需要找father->fail->fail這個節點,然後重複上面過程,
如果一直找都找不到,則child的fail指標就要指向root。
匹配過程分兩種情況:
(1)當前字元匹配,表示從當前節點沿著樹邊有一條路徑可以到達目標字元,
如果當前匹配的字元是乙個單詞的結尾,我們可以沿著當前字元的fail指標,一直遍歷到根,
如果這些節點末尾有標記(標記代表節點是乙個單詞末尾),遍歷到的都是可以匹配上的節點。
統計完畢後標記。此時沿該路徑走向下乙個節點繼續匹配,目標指標移向下個字元。
(2)當前字元不匹配,則去當前節點失敗指標所指向的字元繼續匹配,
匹配過程隨著指標指向root結束。重複這2個過程中的任意乙個,直到模式串走到結尾為止。
對照上圖,看一下模式匹配這個詳細的流程,其中模式串為 yasherhs(某文章)。
對於i=0,1,trie 中沒有對應的路徑,故不做任何操作;
i=2,3,4時,指標p走到左下節點e。因為節點e的 count 資訊為1,所以cnt+1,
(到達某乙個單詞的末尾,該資訊用bool tail [ ] 記錄,單詞數++)。
並且將節點e的 count 值設定為-1,表示改單詞已經出現過了,防止重複計數,
最後 temp 指向e節點的失敗指標所指向的節點繼續查詢,
以此類推,最後 temp 指向 root,退出 while 迴圈,這個過程中 count 增加了2。
表示找到了2個單詞she和he。當i=5時,程式進入第5行,p指向其失敗指標的節點,
也就是右邊那個e節點,隨後在第6行指向r節點,r節點的 count 值為1,
從而count+1,迴圈直到 temp 指向 root 為止。最後i=6,7時,找不到任何匹配,結束。
struct node;
void insert(char *s)
p = p->next[x];
}p->sum++;
}
//trie字典樹:
int tot=1,trie[maxn][26];
bool tail[maxn]; //串尾元素標記
void make_trie(char* s)
p=trie[p][c]; //向下層尋找匹配字元編號
}return;
}(1)【關鍵字搜尋】hdu 2222
#include #include #include #include #include #include #include #include #include using namespace std;
typedef long long ll;
typedef unsigned long long ull;
/*【關鍵字搜尋】hdu 2222
尋找有多少個單詞在文章中出現。 */
// ac自動機模板題
const int maxn=5e5+5;
//trie字典樹:
int ans,tot=1,trie[maxn][30];
int tail[maxn]; //串尾元素標記:0為未出現,1為已出現
void make_trie(char* s)
p=trie[p][c]; //向下層尋找匹配字元編號
}return;
}int main()
bfs(); //建立nextt陣列
scanf("%s",s); //文章
find2(s); printf("%d\n",ans);
}return 0;
}
(2)【單詞】 bzoj 3172
#include #include #include #include #include #include #include #include #include using namespace std;
typedef long long ll;
typedef unsigned long long ull;
/*【單詞】 bzoj 3172
乙個**,求每個單詞分別在**中出現多少次。 */
const int maxn=1000010;
struct nodep[maxn];
int n,m,tot;
int q[maxn],ans[maxn],pos[maxn];
char ss[maxn];
queueq;
int main()
(3)【玄武密碼】bzoj4327 (目測沒有問題,但執行錯誤orz)
#include #include #include #include #include #include #include #include #include using namespace std;
typedef long long ll;
typedef unsigned long long ull;
/*【玄武密碼】bzoj4327
對於每一段文字,求其字首在母串上的最大匹配長度。 */
const int maxn=10000010;
int tot=1,m,n;
char s[maxn],ch[109];
int trie[maxn][4],l[maxn],fa[maxn];
int pos[maxn],point[maxn];
int nextt[maxn],que[maxn];
int cal(char x)
//trie字典樹:
void make_trie(int x){ //insert
l[x]=strlen(ch); int p=1;
for(int i=0;i(4) bzoj 3940 / 1195 / 2938 / 1030
——時間劃過風的軌跡,那個少年,還在等你。
暖 墟 AC自動機 AC自動機的總結與運用
kmp 匹配單串,線性掃瞄,在失配時用next陣列引導j指標回溯,進行下一步匹配。trie樹 多模式的匹配,構造26叉樹,同時記錄多個串的情況,記錄結尾,進行匹配。kmp trie樹 ac自動機 ac自動機 給乙個字典,再給乙個文字,問這個文字裡出現了字典裡的哪些字。可以用n個單詞的n次kmp演算法...
AC 自動機 多模式串匹配
上的敏感詞過濾是怎麼實現的呢?實際上,這些功能最基本的原理就是字串匹配演算法,也就是通過維護乙個敏感詞的字典,當使用者輸入一段文字內容後,通過字串匹配演算法來檢查使用者輸入的內容是否包含敏感詞。bf rk bm kmp 演算法都是針對只有乙個模式串的字串匹配演算法,而要實現乙個高效能的敏感詞過濾系統...
AC自動機(多模式串匹配)
虛線部分代表fail指標 ac自動機模板題鏈結 靜態陣列版本 推薦 量少易寫,更快易寫 include using namespace std class aho corasick 儲存結構 trie樹 void insert const char str cnt p void build fail...