AC自動機學習筆記
阿新 • • 發佈:2020-06-29
原部落格地址:https://www.cnblogs.com/hyfhaha/p/10802604.html
問題的背景
給定n個模式串和1個文字串,求有多少個模式串在文字串裡出現過。
注意:是出現過,就是出現多次只算一次。
我們將n個模式串建成一顆字典樹。但是當我們匹配成功一個模式串後,需要重新回到根做匹配,這樣效率太低了。
比如這個字典樹,我們從4號點匹配失敗後,可以繼續從7號點開始匹配,然後匹配到8。
那麼我們怎麼確定從哪個點開始匹配呢?我們稱i匹配失敗後從j開始匹配,j是i的失配指標。
失配指標的實質含義是什麼?
如果一個點的Fail指標指向j,那麼root到j的字串是root到i的字串的一個字尾。
所以Fail指標的含義是:最長的當前字串的字尾在字典樹上可以找到的末尾編號。
求Fail指標
首先我們可以確定,每一個點i的Fail指標指向的點的深度一定是比i小的。
第一層的Fail一定指的是root。
設點i的父親fa的Fail指標指的是Fafail,那麼如果Fafail有和i值相同的兒子j,那麼i的Fail就指向j。(此時我已經懵逼)
具體實現:
void getFail () { for (int i=0;i<26;i++) Trie[0].son[i]=1; queue<int> q; q.push(1); Trie[1].fail=0; while (!q.empty()) { int u=q.front(); q.pop(); for (int i=0;i<26;i++) { int v=Trie[u].son[i]; int Fail=Trie[u].fail; if (!v) { Trie[u].son[i]=Trie[Fail].son[i]; continue; } Trie[v].fail=Trie[Fail].son[i]; q.push(v); } } }
int query (char * s) { int u=1; int ans=0; int len=strlen(s); for (int i=0;i<len;i++) { int v=s[i]-'a'; int k=Trie[u].son[v]; while (k>1&&Trie[k].f!=-1) { ans+=Trie[k].f; Trie[k].f=-1; k=Trie[k].fail; } u=Trie[u].son[v]; } return ans; }
#include<bits/stdc++.h> using namespace std; const int maxn=2e5+100; struct trie { int son[26]; int f; int fail; }Trie[maxn]; int n,cnt; char s[maxn]; queue<int> q; void insert (char * s) { int u=1; int len=strlen(s); for (int i=0;i<len;i++) { int v=s[i]-'a'; if (!Trie[u].son[v]) Trie[u].son[v]=++cnt; u=Trie[u].son[v]; } Trie[u].f++; } void getFail () { for (int i=0;i<26;i++) Trie[0].son[i]=1; queue<int> q; q.push(1); Trie[1].fail=0; while (!q.empty()) { int u=q.front(); q.pop(); for (int i=0;i<26;i++) { int v=Trie[u].son[i]; int Fail=Trie[u].fail; if (!v) { Trie[u].son[i]=Trie[Fail].son[i]; continue; } Trie[v].fail=Trie[Fail].son[i]; q.push(v); } } } int query (char * s) { int u=1; int ans=0; int len=strlen(s); for (int i=0;i<len;i++) { int v=s[i]-'a'; int k=Trie[u].son[v]; while (k>1&&Trie[k].f!=-1) { ans+=Trie[k].f; Trie[k].f=-1; k=Trie[k].fail; } u=Trie[u].son[v]; } return ans; } int main () { cnt=1; cin>>n; for (int i=1;i<=n;i++) scanf("%s",s),insert(s); getFail(); scanf("%s",s); printf("%d\n",query(s)); return 0; }