AC自动机学习笔记
原博客地址: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; }