日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

学习笔记:AC自动机

發(fā)布時(shí)間:2023/12/18 编程问答 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 学习笔记:AC自动机 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

話說(shuō)AC自動(dòng)機(jī)有什么用......我想要自動(dòng)AC機(jī)

AC自動(dòng)機(jī)簡(jiǎn)介:?

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

AC自動(dòng)機(jī)的構(gòu)造:

1.構(gòu)造一棵Trie,作為AC自動(dòng)機(jī)的搜索數(shù)據(jù)結(jié)構(gòu)。

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

3.掃描主串進(jìn)行匹配。

AC自動(dòng)機(jī)詳講:

我們給出5個(gè)單詞,say,she,shr,he,her。給定字符串為yasherhs。問(wèn)多少個(gè)單詞在字符串中出現(xiàn)過(guò)。

一、Trie

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

首先會(huì)有3個(gè)重要的指針,分別為p, p->fail, temp。

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

2.指針p->fail,p的失敗指針,指向與字符p相同的結(jié)點(diǎn),若沒(méi)有,則指向root。

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

對(duì)于Trie樹(shù)中的一個(gè)節(jié)點(diǎn),對(duì)應(yīng)一個(gè)序列s[1...m]。此時(shí),p指向字符s[m]。若在下一個(gè)字符處失配,即p->next[s[m+1]] == NULL,則由失配指針跳到另一個(gè)節(jié)點(diǎn)(p->fail)處,該節(jié)點(diǎn)對(duì)應(yīng)的序列為s[i...m]。若繼續(xù)失配,則序列依次跳轉(zhuǎn)直到序列為空或出現(xiàn) 匹配。在此過(guò)程中,p的值一直在變化,但是p對(duì)應(yīng)節(jié)點(diǎn)的字符沒(méi)有發(fā)生變化。在此過(guò)程中,我們觀察可知,最終求得得序列s則為最長(zhǎng)公共后綴。另外,由于這個(gè) 序列是從root開(kāi)始到某一節(jié)點(diǎn),則說(shuō)明這個(gè)序列有可能是某些序列的前綴。

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

對(duì)于已經(jīng)得到的序列s[1...m],由于s[i...m]可能是某單詞的后綴,s[1...j]可能是某單詞的前綴,所以s[1...m]中可能會(huì)出現(xiàn) 單詞。此時(shí),p指向已匹配的字符,不能動(dòng)。于是,令temp = p,然后依次測(cè)試s[1...m], s[i...m]是否是單詞。

構(gòu)造的Trie為:


二、構(gòu)造失敗指針

用BFS來(lái)構(gòu)造失敗指針,與KMP算法相似的思想。

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


三、掃描

構(gòu)造好Trie和失敗指針后,我們就可以對(duì)主串進(jìn)行掃描了。這個(gè)過(guò)程和KMP算法很類似,但是也有一定的區(qū)別,主要是因?yàn)锳C自動(dòng)機(jī)處理的是多串模式,需要防止遺漏某個(gè)單詞,所以引入temp指針。

匹配過(guò)程分兩種情況:(1)當(dāng)前字符匹配,表示從當(dāng)前節(jié)點(diǎn)沿著樹(shù)邊有一條路徑可以到達(dá)目標(biāo)字符,此時(shí)只需沿該路徑走向下一個(gè)節(jié)點(diǎn)繼續(xù)匹配即可,目標(biāo) 字符串指針移向下個(gè)字符繼續(xù)匹配;(2)當(dāng)前字符不匹配,則去當(dāng)前節(jié)點(diǎn)失敗指針?biāo)赶虻淖址^續(xù)匹配,匹配過(guò)程隨著指針指向root結(jié)束。重復(fù)這2個(gè)過(guò)程 中的任意一個(gè),直到模式串走到結(jié)尾為止。

?對(duì)照上圖,看一下模式匹配這個(gè)詳細(xì)的流程,其中模式串為yasherhs。對(duì)于i=0,1。Trie中沒(méi)有對(duì)應(yīng)的路徑,故不做任何操 作;i=2,3,4時(shí),指針p走到左下節(jié)點(diǎn)e。因?yàn)楣?jié)點(diǎn)e的count信息為1,所以cnt+1,并且講節(jié)點(diǎn)e的count值設(shè)置為-1,表示改單詞已經(jīng) 出現(xiàn)過(guò)了,防止重復(fù)計(jì)數(shù),最后temp指向e節(jié)點(diǎn)的失敗指針?biāo)赶虻墓?jié)點(diǎn)繼續(xù)查找,以此類推,最后temp指向root,退出while循環(huán),這個(gè)過(guò)程中 count增加了2。表示找到了2個(gè)單詞she和he。當(dāng)i=5時(shí),程序進(jìn)入第5行,p指向其失敗指針的節(jié)點(diǎn),也就是右邊那個(gè)e節(jié)點(diǎn),隨后在第6行指向r 節(jié)點(diǎn),r節(jié)點(diǎn)的count值為1,從而count+1,循環(huán)直到temp指向root為止。最后i=6,7時(shí),找不到任何匹配,匹配過(guò)程結(jié)束。

到此,AC自動(dòng)機(jī)入門知識(shí)就講完了。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

?

轉(zhuǎn)載于:https://www.cnblogs.com/SBSOI/p/5681998.html

總結(jié)

以上是生活随笔為你收集整理的学习笔记:AC自动机的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。