學習筆記:AC自動機

話說AC自動機有什么用......我想要自動AC機

AC自動機簡介:?

首先簡要介紹一下AC自動機:Aho-Corasick automation,該算法在1975年產生于貝爾實驗室,是著名的多模匹配算法之一。一個常見的例子就是給出n個單詞,再給出一段包含m個字符的文 章,讓你找出有多少個單詞在文章里出現過。要搞懂AC自動機,先得有字典樹Trie和KMP模式匹配算法的基礎知識。KMP算法是單模式串的字符匹配算 法,AC自動機是多模式串的字符匹配算法。

AC自動機的構造:

1.構造一棵Trie,作為AC自動機的搜索數據結構。

2.構造fail指針,使當前字符失配時跳轉到具有最長公共前后綴的字符繼續匹配。如 同 KMP算法一樣, AC自動機在匹配時如果當前字符匹配失敗,那么利用fail指針進行跳轉。由此可知如果跳轉,跳轉后的串的前綴,必為跳轉前的模式串的后綴并且跳轉的新位 置的深度(匹配字符個數)一定小于跳之前的節點。所以我們可以利用 bfs在 Trie上面進行 fail指針的求解。

3.掃描主串進行匹配。

AC自動機詳講:

我們給出5個單詞,say,she,shr,he,her。給定字符串為yasherhs。問多少個單詞在字符串中出現過。

一、Trie

首先我們需要建立一棵Trie。但是這棵Trie不是普通的Trie,而是帶有一些特殊的性質。

首先會有3個重要的指針,分別為p, p->fail, temp。

1.指針p,指向當前匹配的字符。若p指向root,表示當前匹配的字符序列為空。(root是Trie入口,沒有實際含義)。

2.指針p->fail,p的失敗指針,指向與字符p相同的結點,若沒有,則指向root。

3.指針temp,測試指針(自己命名的,容易理解!~),在建立fail指針時有尋找與p字符匹配的結點的作用,在掃描時作用最大,也最不好理解。

對于Trie樹中的一個節點,對應一個序列s[1...m]。此時,p指向字符s[m]。若在下一個字符處失配,即p->next[s[m+1]] == NULL,則由失配指針跳到另一個節點(p->fail)處,該節點對應的序列為s[i...m]。若繼續失配,則序列依次跳轉直到序列為空或出現 匹配。在此過程中,p的值一直在變化,但是p對應節點的字符沒有發生變化。在此過程中,我們觀察可知,最終求得得序列s則為最長公共后綴。另外,由于這個 序列是從root開始到某一節點,則說明這個序列有可能是某些序列的前綴。

再次討論p指針轉移的意義。如果p指針在某一字符s[m+1]處失配(即p->next[s[m+1]] == NULL),則說明沒有單詞s[1...m+1]存在。此時,如果p的失配指針指向root,則說明當前序列的任意后綴不會是某個單詞的前綴。如果p的失 配指針不指向root,則說明序列s[i...m]是某一單詞的前綴,于是跳轉到p的失配指針,以s[i...m]為前綴繼續匹配s[m+1]。

對于已經得到的序列s[1...m],由于s[i...m]可能是某單詞的后綴,s[1...j]可能是某單詞的前綴,所以s[1...m]中可能會出現 單詞。此時,p指向已匹配的字符,不能動。于是,令temp = p,然后依次測試s[1...m], s[i...m]是否是單詞。

構造的Trie為:


二、構造失敗指針

用BFS來構造失敗指針,與KMP算法相似的思想。

首先,root入隊,第1次循環時處理與root相連的字符,也就是各個單詞的第一個字符h和s,因為第一個字符不匹配需要重新匹配,所以第一個字符都指 向root(root是Trie入口,沒有實際含義)失敗指針的指向對應下圖中的(1),(2)兩條虛線;第2次進入循環后,從隊列中先彈出h,接下來p 指向h節點的fail指針指向的節點,也就是root;p=p->fail也就是p=NULL說明匹配序列為空,則把節點e的fail指針指向 root表示沒有匹配序列,對應圖-2中的(3),然后節點e進入隊列;第3次循環時,彈出的第一個節點a的操作與上一步操作的節點e相同,把a的 fail指針指向root,對應圖-2中的(4),并入隊;第4次進入循環時,彈出節點h(圖中左邊那個),這時操作略有不同。由于 p->next[i]!=NULL(root有h這個兒子節點,圖中右邊那個),這樣便把左邊那個h節點的失敗指針指向右邊那個root的兒子節點 h,對應圖-2中的(5),然后h入隊。以此類推:在循環結束后,所有的失敗指針就是圖-2中的這種形式。


三、掃描

構造好Trie和失敗指針后,我們就可以對主串進行掃描了。這個過程和KMP算法很類似,但是也有一定的區別,主要是因為AC自動機處理的是多串模式,需要防止遺漏某個單詞,所以引入temp指針。

匹配過程分兩種情況:(1)當前字符匹配,表示從當前節點沿著樹邊有一條路徑可以到達目標字符,此時只需沿該路徑走向下一個節點繼續匹配即可,目標 字符串指針移向下個字符繼續匹配;(2)當前字符不匹配,則去當前節點失敗指針所指向的字符繼續匹配,匹配過程隨著指針指向root結束。重復這2個過程 中的任意一個,直到模式串走到結尾為止。

?對照上圖,看一下模式匹配這個詳細的流程,其中模式串為yasherhs。對于i=0,1。Trie中沒有對應的路徑,故不做任何操 作;i=2,3,4時,指針p走到左下節點e。因為節點e的count信息為1,所以cnt+1,并且講節點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時,找不到任何匹配,匹配過程結束。

到此,AC自動機入門知識就講完了。HDU 2222入門題必須果斷A掉。bzoj3172也要A。

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<cmath>
 5 #include<algorithm>
 6 #include<queue>
 7 #include<cstdlib>
 8 #include<iomanip>
 9 #include<cassert>
10 #include<climits>
11 #include<vector>
12 #include<list>
13 #define maxn 1000001
14 #define F(i,j,k) for(int i=j;i<=k;i++)
15 #define M(a,b) memset(a,b,sizeof(a))
16 #define FF(i,j,k) for(int i=j;i>=k;i--)
17 #define inf 0x7fffffff
18 #define maxm 2016
19 #define mod 1000000007
20 //#define LOCAL
21 using namespace std;
22 int read(){
23     int x=0,f=1;char ch=getchar();
24     while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
25     while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
26     return x*f;
27 }
28 int pos[maxn];
29 struct AC_automation
30 {
31     int cnt;
32     int next[maxn][26],sum[maxn],fail[maxn],q[maxn];
33     char ch[maxn];
34     AC_automation()
35     {
36         cnt=1;
37         F(i,0,25) next[0][i]=1;
38     }
39     void insert(int &pos)
40     {
41         int now=1;
42         cin>>ch;
43         int len=strlen(ch)-1;
44         F(i,0,len){
45             if(!next[now][ch[i]-'a']) next[now][ch[i]-'a']=++cnt;
46             now=next[now][ch[i]-'a'];
47             sum[now]++;
48         }
49         pos=now;
50     }
51     void build_fail()
52     {
53         int head=0,tail=1;
54         q[0]=1;
55         fail[1]=0;
56         while(head<tail)
57         {
58             int now=q[head];
59             head++;
60             F(i,0,25){
61                 int v=next[now][i];
62                 if(!v) continue;
63                 int k=fail[now];
64                 while(!next[k][i]) k=fail[k];
65                 fail[v]=next[k][i];
66                 q[tail++]=v;
67             }
68         }
69         FF(i,tail-1,0){
70             sum[fail[q[i]]]+=sum[q[i]];
71         }
72     }
73 }ac;
74 long long n,m;
75 int main()
76 {
77     std::ios::sync_with_stdio(false);//cout<<setiosflags(ios::fixed)<<setprecision(1)<<y;
78     #ifdef LOCAL
79     freopen("data.in","r",stdin);
80     freopen("data.out","w",stdout);
81     #endif
82     cin>>n;
83     F(i,1,n){
84         ac.insert(pos[i]);
85     }
86     ac.build_fail();
87     F(i,1,n){
88         cout<<ac.sum[pos[i]]<<endl;
89     }
90     return 0;
91 }
bzoj 3172

?

轉載于:https://www.cnblogs.com/SBSOI/p/5681998.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/457586.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/457586.shtml
英文地址,請注明出處:http://en.pswp.cn/news/457586.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

python閉包和裝飾器

DAY 9. 閉包和裝飾器 9.1 閉包 閉包就是內部函數對外部函數作用域內變量的引用 可以看出 閉包是針對函數的&#xff0c;還有兩個函數&#xff0c;內部函數和外部函數閉包是為了讓內部函數引用外部函數作用域內的變量的 我們先寫兩個函數 def fun1():print("我是fun1&q…

學歷是銅牌,能力是銀牌,人脈是金牌,思維是王牌

有人工作&#xff0c;有人上學&#xff0c;大家千萬不要錯過這篇文章&#xff0c;能看到這篇文章也是一種幸運&#xff0c;真的受益匪淺&#xff0c;對我有很大啟迪&#xff0c;這篇文章將會改變你我的一生&#xff0c;真的太好了&#xff0c;希望與有緣人分享&#xff0c;也希…

石頭剪刀布python編程_《python核心編程第二版》練習題——游戲:石頭剪刀布

習題里比較有意思的一個題目&#xff0c;實現石頭剪刀布這個游戲&#xff0c;起初設計的時候走彎路了(主要時被習題里那個“盡量少用if判斷”給整暈了)&#xff0c;想的太復雜&#xff0c;后來發現其實非常簡單&#xff0c;完全可以不寫if語句。還是枚舉法&#xff1a;#! /usr/…

SpringMvc面試題

f-sm-1. 講下SpringMvc和Struts1,Struts2的比較的優勢 性能上Struts1>SpringMvc>Struts2 開發速度上SpringMvc和Struts2差不多,比Struts1要高f-sm-2. 講下SpringMvc的核心入口類是什么,Struts1,Struts2的分別是什么 SpringMvc的是DispatchServlet,Struts1的是ActionServl…

python 鴨子類型

DAY 10. 鴨子類型 這個概念來源于美國印第安納州的詩人詹姆斯惠特科姆萊利&#xff08;James Whitcomb Riley,1849-1916&#xff09;的詩句&#xff1a;”When I see a bird that walks like a duck and swims like a duck and quacks like a duck, I call that bird a duck.”…

thinkphp一句話疑難解決筆記

URL_PATHINFO_DEPR, depr表示 網頁路徑"分隔符",用"-", 有利于seo,注意是從 sername/index.php(開始的)/home-user-login-var-value開始的,pathinfo也支持普通的參數傳值(僅僅支持參數...). 在thinkphp中,有兩個地方使用depr,另一個就是tpl的文件目錄組織分…

python選取特定行_pandas.DataFrame選取/排除特定行的方法

pandas.DataFrame選取特定行使用Python進行數據分析時&#xff0c;經常要使用到的一個數據結構就是pandas的DataFrame&#xff0c;如果我們想要像Excel的篩選那樣&#xff0c;只要其中的一行或某幾行&#xff0c;可以使用isin()方法&#xff0c;將需要的行的值以列表方式傳入&a…

學校選址_洛谷U3451_帶權中位數

題目描述 在一條大路一旁有許多棟樓&#xff0c;每棟樓里有許多小學生&#xff08;哈哈哈一波小學生來襲&#xff01;&#xff09;。但是這條路上沒有小學&#xff01;&#xff01;&#xff01;&#xff01;所以唯恐世界不亂的牛A打算在路上&#xff08;汽車什么的都不敢來這個…

python 重載的實現(single-dispatch generic function)

DAY 11. python 重載 函數重載是指允許定義參數數量或類型不同的同名函數&#xff0c;程序在運行時會根據所傳遞的參數類型選擇應該調用的函數 &#xff0c;但在默認情況下&#xff0c;python是不支持函數重載的&#xff0c;定義同名函數會發生覆蓋 def foo(a:int):print(fin…

SQL中的多表查詢,以及JOIN的順序重要么?

說法是&#xff0c;一般來說&#xff0c;JOIN的順序不重要&#xff0c;除非你要自己定制driving table。 示例&#xff1a; SELECT a.account_id, c.fed_id, e.fname, e.lname-> FROM account AS a INNER JOIN customer AS c-> ON a.cust_id c.cust_id-> INNER JOIN …

python可變對象 不可變對象_【Python】可變對象和不可變對象

在 Python 中一切都可以看作為對象。每個對象都有各自的 id, type 和 value。id: 當一個對象被創建后&#xff0c;它的 id 就不會在改變&#xff0c;這里的 id 其實就是對象在內存中的地址&#xff0c;可以使用 id() 去查看對象在內存中地址。type: 和 id 一樣當對象唄創建之后…

MySQL 調優基礎(三) Linux文件系統

Linux的文件系統有點像MySQL的存儲引擎&#xff0c;它支持各種各樣的文件系統。它最上層是通過 virtual files system虛擬文件系統作為一個抽象接口層來對外提供調用的。然后下層的各種文件系統實現這些調用接口就行了。 1. Linux 中的 日志文件系統和非日志文件系統 文件內容的…

python 經典類和新式類

DAY 12. python新式類和舊式類 繼承自object基類的類叫做新式類&#xff0c;否則叫做舊式類&#xff0c;python3中的類默認是新式類&#xff0c;之前版本默認是舊式類 rootkail:~# python python 2.7.15 (default,Jul 28 2018,11:29:29) [GCC 8.1.0] on linux2 Type "he…

Why does pthread_cond_signal not work?【轉】

轉自&#xff1a;http://stackoverflow.com/questions/16819169/why-does-pthread-cond-signal-not-work# 0 down vote favorite I am currently learing all around POSIX threads (pthread). I now have created a simple program which increased a shared value by 7 until…

Android開發技術周報 Issue#72

新聞 Android N 最初預覽版&#xff1a;開發者 API 和工具教程 Gradle依賴的統一管理 理解Java垃圾回收機制 淺談 Android 編程思想和架構 由Android 65K方法數限制引發的思考 Android音頻開發&#xff08;1&#xff09;&#xff1a;基礎知識 Android音頻開發&#xff08;…

python 單例模式的四種實現方法

DAY 13. 單例設計 13.1 什么是單例設計 一個類每次實例化返回的都是同一個對象&#xff0c;這種設計模式叫做單例設計&#xff0c;這個類叫做單例類 13.2 實現單例設計的方法 13.2.1 重寫__new__() class Foo:def __new__(cls,*args, **kwargs):# 如果是第一次實例化&…

Redis3.2.5部署(單節點)

1.安裝jdk1.8 [rootsht-logstash-01 ~]# cd /usr/java/ [rootsht-logstash-01 java]# wget --no-check-certificate --no-cookies --header "Cookie: oraclelicenseaccept-securebackup-cookie" http://download.oracle.com/otn-pub/java/jdk/8u111-b14/jdk-8u111…

字節跳動 設計模式 pdf_憑這份pdf我拿下了美團、字節跳動、阿里、小米等大廠的offer...

關于程序員&#xff0c;除了做項目來提高自身的技術之外&#xff0c;還有一種提升自己的專業技能就是&#xff1a;多&#xff01;看&#xff01;書&#xff01;小編整理出一篇Java進階架構師之路的核心知識&#xff0c;同時也是面試時面試官必問的知識點&#xff0c;篇章也是包…

B. One Bomb (#363 Div.2)

B. One Bombtime limit per test1 secondmemory limit per test256 megabytesinputstandard inputoutputstandard outputYou are given a description of a depot. It is a rectangular checkered field of n??m size. Each cell in a field can be empty (".") or…

力扣交替打印FooBar

這道題要注意的是兩個線程喚醒和等待的順序&#xff0c;應為第一個線程會比第二個線程更早結束&#xff0c;所以如果第一個線程已經結束&#xff0c;而第二個線程還在等待被喚醒&#xff0c;那第二個線程會一直等待下去&#xff0c;因此第一個線程要先等待后喚醒&#xff0c;這…