國外玩家爺爺打通《質量效應:傳奇版》 30年來最好的樂子
阿新 • • 發佈:2021-07-04
AC自動機分為三個部分:
-
構造trie樹
-
構造fail指標
-
模式匹配
1.構造trie樹
在trie樹上,從根節點開始,到某一葉子節點的路徑即為一個字串。
每個節點有n個出度,代表字串每一位有n種情況。
如,對於二進位制數,n=2 ;對於小寫字母字串,n=26 。
因此,構建trie樹,只需從根節點開始,沿著字串向下移動,並不斷補充新節點即可。
最後在葉子節點上進行相應的處理即可(具體處理方法根據題意)。
以二進位制為例,trie樹處理結果如圖:
程式碼:
void build_trie(char x[]){ int len=strlen(x),p=0; for(int i=0;i<len;++i){ int t=x[i]-'a'; if(!pre[p][t])pre[p][t]=++tot; p=pre[p][t]; } ++val[p]; }
2.構造fail指標
KMP只支援 單串與單串 之間的比較,而AC自動機通過新增fail指標,記錄最大相同字首字尾,滿足 單串與多串 的比較。
對於某一節點,其子節點的fail為其fail的相同子節點。
特別的,根節點的子節點的fail為根節點。
建立了fail指標後,當某個子節點不存在時,就可以順著fail往上跳,保證連貫性。
以二進位制為例,fail指標處理結果如圖:
程式碼:
void build_AC(){ for(int i=0;i<26;++i) if(pre[0][i])q.push(pre[0][i]); while(!q.empty()){ int x=q.front();q.pop(); for(int i=0;i<26;++i){ if(pre[x][i]){ q.push(pre[x][i]); fail[pre[x][i]]=pre[fail[x]][i]; } else pre[x][i]=pre[fail[x]][i]; } } }
————————————————————
3.模式匹配
準備工作做完後,接下來開始匹配。
同樣是順著字串往下走,與第一步不同的是,這次不能建立新節點,同時要關注fail。
因為fail記錄的是最大相同字首字尾,則fail的fail記錄的是所有的相同字首字尾。
因此,對於一個節點,只要一直往上fail,就可以訪問到所有可能存在的答案。
程式碼:
int query_AC(char x[]){ int len=strlen(x),p=0,res=0; for(int i=0;i<len;++i){ int t=x[i]-'a'; p=pre[p][t]; for(int j=p;j && ~val[j];j=fail[j]){ res+=val[j];val[j]=-1; } } return res; }
完成以上三個步驟,AC自動機就建好了。
完整程式碼:
#include<bits/stdc++.h>
using namespace std;
const int M=1e6+5;
int n;
char s[M];
queue<int> q;
struct AC_AUTO{
int pre[M][30],fail[M],val[M],tot;
void build_trie(char x[]){
int len=strlen(x),p=0;
for(int i=0;i<len;++i){
int t=x[i]-'a';
if(!pre[p][t])pre[p][t]=++tot;
p=pre[p][t];
}
++val[p];
}
void build_AC(){
for(int i=0;i<26;++i)
if(pre[0][i])q.push(pre[0][i]);
while(!q.empty()){
int x=q.front();q.pop();
for(int i=0;i<26;++i){
if(pre[x][i]){
q.push(pre[x][i]);
fail[pre[x][i]]=pre[fail[x]][i];
}
else pre[x][i]=pre[fail[x]][i];
}
}
}
int query_AC(char x[]){
int len=strlen(x),p=0,res=0;
for(int i=0;i<len;++i){
int t=x[i]-'a';
p=pre[p][t];
for(int j=p;j && ~val[j];j=fail[j]){
res+=val[j];val[j]=-1;
}
}
return res;
}
}AC;
int main(){
scanf("%d",&n);
for(int i=1;i<=n;++i){
scanf("%s",s);
AC.build_trie(s);
}
AC.build_AC();
scanf("%s",s);
printf("%d\n",AC.query_AC(s));
return 0;
}
\[\mathcal{By}\quad\mathcal{Most}\ \mathcal{Handsome}
\]