1. 程式人生 > >「LuoguP3808」 【模板】AC自動機(簡單版)

「LuoguP3808」 【模板】AC自動機(簡單版)

取數據 length 默認 是個 www str sum 題目 turn

題目背景

通過套取數據而直接“打表”過題者,是作弊行為,發現即棕名。

這是一道簡單的AC自動機模板題。

用於檢測正確性以及算法常數。

為了防止卡OJ,在保證正確的基礎上只有兩組數據,請不要惡意提交。

管理員提示:本題數據內有重復的單詞,且重復單詞應該計算多次,請各位註意

題目描述

給定n個模式串和1個文本串,求有多少個模式串在文本串裏出現過。

輸入輸出格式

輸入格式:

第一行一個n,表示模式串個數;

下面n行每行一個模式串;

下面一行一個文本串。

輸出格式:

一個數表示答案

輸入輸出樣例

輸入樣例#1: 復制
2
a
aa
aa
輸出樣例#1: 復制
2

說明

subtask1[50pts]:∑length(模式串)<=10^6,length(文本串)<=10^6,n=1;

subtask2[50pts]:∑length(模式串)<=10^6,length(文本串)<=10^6;


題解

在通常就沒什麽人認真聽的集訓裏,qwerta剛擼完一把Win7原裝的掃雷,舒適的擡起頭,正好對上了老師講fail樹的眼神。

所以是懂一丟丟手推方法的。

建議大家先構造點小數據手推,像是什麽在$her,him,he,his,she$上匹配$shehisher$之類的。

網上教程也蠻多,就不畫圖了。(懶

其實是自己也推不蠻清楚

然後是教材QAQ yyb大佬的博客

總之,AC自動機就是在trie樹上bfs一下然後亂搞,可以理解為trie上的KMP叭。

自己都不怎麽會,就不講這個東西了QAQ

 1 #include<queue>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<iostream>
 5 using namespace std;
 6 #define R register
 7 const int MAXL=1e6+3
; 8 char s[MAXL]; 9 struct emm{ 10 int fail; 11 int nxt[26]; 12 int tag; 13 }AC[MAXL];//Tree結構體 14 queue<int>q;//get_fail的時候用的queue 15 int main() 16 { 17 //freopen("a.in","r",stdin); 18 ios::sync_with_stdio(false); 19 cin.tie(false);cout.tie(false); 20 int n; 21 cin>>n; 22 int tot=0; 23 //trie 24 while(n--) 25 { 26 cin>>s; 27 int len=strlen(s); 28 int now=0;//now表示當前的節點編號 29 for(R int i=0;i<len;++i) 30 { 31 if(!AC[now].nxt[s[i]-a])//如果當前節點沒有這個兒子 32 AC[now].nxt[s[i]-a]=++tot;//就造個兒子 33 now=AC[now].nxt[s[i]-a];//now跳到這個兒子上 34 } 35 AC[now].tag++;//now最後在的地方是這個模式串的結束點,標記一下 36 } 37 //get_fail 38 { 39 //首先把跟0號節點相連的點處理一下 40 for(R int i=0;i<26;++i) 41 { 42 if(AC[0].nxt[i])//如果這個兒子存在 43 { 44 AC[AC[0].nxt[i]].fail=0;//把fail指向0號節點(其實默認就是0啊QAQ 45 q.push(AC[0].nxt[i]);//把這個兒子push進去 46 } 47 } 48 while(!q.empty()) 49 { 50 int x=q.front();q.pop();//取點 51 for(R int i=0;i<26;++i) 52 { 53 if(AC[x].nxt[i])//如果這個兒子存在 54 { 55 AC[AC[x].nxt[i]].fail=AC[AC[x].fail].nxt[i];//敲重點!!! 56 //這個兒子的fail,等於這個節點fail的兒子 會手推就能懂叭QAQ 57 q.push(AC[x].nxt[i]);//push進去 58 } 59 else 60 AC[x].nxt[i]=AC[AC[x].fail].nxt[i];//這裏直接把nxt指向了fail的對應兒子 61 } 62 } 63 } 64 //run 65 cin>>s; 66 long long ans=0; 67 int len=strlen(s); 68 int now=0;//now表示當前節點編號 69 for(R int i=0;i<len;++i) 70 { 71 now=AC[now].nxt[s[i]-a];//往下走一層(因為之前直接把空的nxt往fail指了,所以不需要通常的while跳fail 72 //大概就是Trie圖和Trie樹的區別了叭 73 for(R int t=now;t&&AC[t].tag!=-1;t=AC[t].fail)//從這個點開始暴力跳fail,找匹配 74 { 75 ans+=AC[t].tag; 76 AC[t].tag=-1;//這是個剪枝 77 } 78 } 79 cout<<ans; 80 return 0;//撒花 81 }

然後吐槽一下咕咕的數據,之前有一大段把$now$和$i$混用了,結果還過了一個點(???

「LuoguP3808」 【模板】AC自動機(簡單版)