日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 人文社科 > 生活经验 >内容正文

生活经验

后缀自动机详解

發(fā)布時(shí)間:2023/11/27 生活经验 72 豆豆
生活随笔 收集整理的這篇文章主要介紹了 后缀自动机详解 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
?

后綴自動(dòng)機(jī)詳解

標(biāo)簽:?后綴自動(dòng)機(jī) ?2341人閱讀?評論(3)?收藏?舉報(bào) ?分類:

后綴自動(dòng)機(jī)

后綴自動(dòng)機(jī)(單詞的有向無環(huán)圖)——是一種強(qiáng)有力的數(shù)據(jù)結(jié)構(gòu),讓你能夠解決許多字符串問題。

例如,使用后綴自動(dòng)機(jī)可以在某一字符串中搜索另一字符串的所有出現(xiàn)位置,或者計(jì)算不同子串的個(gè)數(shù)——這都能在線性

時(shí)間內(nèi)解決。

? ?直覺上,后綴自動(dòng)機(jī)可以被理解為所有子串的簡明信息。一個(gè)重要的事實(shí)是,后綴自動(dòng)機(jī)以壓縮后的形式包含了一個(gè)長度

為n的字符串的所有信息,僅需要O(n)的空間。并且,它能在O(n)時(shí)間內(nèi)被構(gòu)造(如果我們將字母表的大小k視作常數(shù),否則就

是O(n*logk))。

? ?歷史上,Blumer等人于1983年首次提出了后綴自動(dòng)機(jī)的線性規(guī)模,然后在1985-1986年,人們提出了首個(gè)線性時(shí)間內(nèi)構(gòu)建

后綴自動(dòng)機(jī)的算法(Crochemore,Blumer等)。在文末鏈接處查看更多細(xì)節(jié)。

? ?后綴自動(dòng)機(jī)在英文中被稱作“suffix automaton”(復(fù)數(shù)形式:suffix automata),單詞的有向無環(huán)圖——"direcged acyclic

word graph"(簡寫為“DAWG”)。


后綴自動(dòng)機(jī)的定義

定義.對給定字符串s的后綴自動(dòng)機(jī)是一個(gè)最小化確定有限狀態(tài)自動(dòng)機(jī),它能夠接收字符串s的所有后綴。


下面解釋這一定義:

·????????后綴自動(dòng)機(jī)是一張有向無環(huán)圖,其中頂點(diǎn)是狀態(tài),而邊代表了狀態(tài)之間的轉(zhuǎn)移。

·????????某一狀態(tài)t_0被稱作初始狀態(tài),由它能夠到達(dá)其余所有狀態(tài)。

·????????自動(dòng)機(jī)中的所有轉(zhuǎn)移——即有向邊——都被某種符號(hào)標(biāo)記。從某一狀態(tài)出發(fā)的諸轉(zhuǎn)移必須擁有不同的標(biāo)記。(另一方面,

狀態(tài)轉(zhuǎn)移不能在任何字符上)。

·????????一個(gè)或多個(gè)狀態(tài)被標(biāo)記為終止?fàn)顟B(tài)。如果我們從初始狀態(tài)t_0經(jīng)由任意路徑走到某一終止?fàn)顟B(tài),并順序?qū)懗鏊薪?jīng)過邊的

標(biāo)記,你得到的字符串必然是s的某一后綴。

·????????在符合上述諸條件的所有自動(dòng)機(jī)中,后綴自動(dòng)機(jī)有這最少的頂點(diǎn)數(shù)。(后綴自動(dòng)機(jī)并不被要求擁有最少的邊數(shù))


后綴自動(dòng)機(jī)的最簡性質(zhì)

最簡性——后綴自動(dòng)機(jī)的最重要性質(zhì)是:它包含了所有s的子串的信息。換言之,對于任意從初始狀態(tài)t_0出發(fā)的路徑,如果我們

寫出所經(jīng)過邊上的標(biāo)記,形成的子串必須是s的子串。相應(yīng)地,s的任意子串都對應(yīng)一條從初始狀態(tài)t_0出發(fā)的路徑。

為了簡化說明,我們稱子串“匹配”了從初始狀態(tài)出發(fā)的路徑,如果該路徑上的邊標(biāo)記組成了這一子串。相應(yīng)地,我們稱任意路徑

“匹配”某一子串,該子串由路徑中邊的標(biāo)記組成。

?后綴自動(dòng)機(jī)的每個(gè)狀態(tài)都引領(lǐng)一條或多條從初始狀態(tài)出發(fā)的路徑。我們稱這個(gè)狀態(tài)有若干匹配這些路徑的方法。

?


構(gòu)建后綴自動(dòng)機(jī)的實(shí)例

下面給出一些對簡單的字符串構(gòu)建后綴自動(dòng)機(jī)的例子。

?始狀態(tài)被記作t0,終止?fàn)顟B(tài)用星號(hào)(*)標(biāo)記。

s=""


s="a"


s="aa"


s="ab"


s="aba"


s="abb"


s="abbb"



一個(gè)線性時(shí)間構(gòu)建后綴自動(dòng)機(jī)的算法

在我們描述構(gòu)建算法之前,有必要介紹一些新的概念和簡要的證明,它們對理解后綴自動(dòng)機(jī)的概念十分重要。

?

結(jié)束位置endpos,它們的性質(zhì)及與后綴自動(dòng)機(jī)的聯(lián)系:

考慮字符串s的任意非空子串t。我們稱終點(diǎn)集合endpos(t)為:s中所有是t出現(xiàn)位置終點(diǎn)的集合。

我們稱兩個(gè)子串t_1和t_2“終點(diǎn)等價(jià)”,如果它們的終點(diǎn)集合一致:endpos(t_1)=endpos(t_2)。因此,所有s的非空子串可

以根據(jù)終點(diǎn)等價(jià)性分成若干類。

?事實(shí)上對后綴自動(dòng)機(jī),終點(diǎn)等價(jià)字符串仍然保持相同性質(zhì)。換句話說,后綴自動(dòng)機(jī)中狀態(tài)數(shù)等價(jià)于所有子串的終點(diǎn)等價(jià)類

個(gè)數(shù),加上初始狀態(tài)。每個(gè)狀態(tài)對應(yīng)一個(gè)或多個(gè)擁有相同終點(diǎn)集合的子串。

? 我們將這一陳述作為假定,然后描述一個(gè)基于此假設(shè)的,線性時(shí)間構(gòu)建后綴自動(dòng)機(jī)的算法——正如我們不久后將會(huì)看到的,

所有后綴自動(dòng)機(jī)的必須性質(zhì),除最小性(即最少頂點(diǎn)數(shù)),都將被滿足(最小性由Nerode產(chǎn)生,見參考文獻(xiàn))。

?關(guān)于終點(diǎn)集合,我們給出一些簡單但重要的事實(shí)。


引理1.兩個(gè)非空子串u和v(length(u)<=length(v))是終點(diǎn)等價(jià)的,當(dāng)且僅當(dāng)u在字符串s中僅作為w的后綴出現(xiàn)。

?

證明是顯然的。

?

引理2.考慮兩個(gè)非空子集u,w(length(u)<=length(w))。它們的終點(diǎn)集合不相交,或者endpos(w)是endpos(u)的子集。進(jìn)一

步地,這取決于u是否是w的后綴:



證明.假設(shè)兩個(gè)集合endpos(u)和endpos(w)有至少一個(gè)公共元素,這就意味著字符串w和u在同一位置結(jié)束,即u是w的后綴。

因此,在字符串w的每次出現(xiàn)的終點(diǎn)u都會(huì)出現(xiàn),這就意味著endpos(w)包含于endpos(u)。

?

引理3.考慮一個(gè)終點(diǎn)等價(jià)類。將該等價(jià)類中的子串按長度遞減排序。排序后的序列中,每個(gè)子串將比上一個(gè)子串短,從而是

上一個(gè)字串的后綴。換句話說,某一終點(diǎn)等價(jià)類中的字符串互為后綴,它們的長度依次取區(qū)間[x,y]內(nèi)的所有數(shù)。

?

證明.考慮這個(gè)終點(diǎn)等價(jià)類。如果它只包含一個(gè)子串,那么引理3的正確性顯然。假設(shè)現(xiàn)在子串的個(gè)數(shù)多于一個(gè)。

?

根據(jù)引理1,兩個(gè)不同的終點(diǎn)等價(jià)子串總滿足一個(gè)是另一個(gè)的嚴(yán)格后綴。因此,在同一終點(diǎn)等價(jià)類中的子串不可能有相同的長

度。

?令w較長,u是等價(jià)類中的最短子串。根據(jù)引理1,u是w的嚴(yán)格后綴。考慮w任意一個(gè)長度為[length(u),length(w)]之間的后綴,

由引理1,顯然它在終點(diǎn)等價(jià)類中。

?

后綴鏈接

考慮一個(gè)狀態(tài)v≠t_0.就我們目前所知,有一個(gè)確定的子串集合,其中元素和v有著相同的終點(diǎn)集合。并且,如果我們記w是其

中的最長者,其余子串均是w的后綴。我們還知道w的前幾個(gè)后綴(按照長度降序)在同一個(gè)終點(diǎn)等價(jià)類中,其余后綴(至少包括

空后綴)在別的終點(diǎn)等價(jià)類中。令t是第一個(gè)這樣的后綴——對它我們建立后綴鏈接。

? 換言之,v的后綴鏈接link(v)指向在不同等價(jià)類中的w的最長后綴。

?在此我們假設(shè)初始狀態(tài)t_0在一個(gè)單獨(dú)的終點(diǎn)等價(jià)類中(僅包含空字符串),并且endpos(t_0)={-1,...,length(s)-1}。

?

引理4.后綴鏈接組成了一棵以t_0為根的樹。

?

證明.考慮任意狀態(tài)v≠t_0.后綴鏈接link(v)指向的狀態(tài)所對應(yīng)的字符串長度嚴(yán)格小于它本身(根據(jù)后綴鏈接的定義和引理3)。

因此,沿著后綴鏈接移動(dòng),我們將早晚到達(dá)t_0,它對應(yīng)一個(gè)空串。

?

引理5.如果我們將所有合法的終點(diǎn)集合建成一棵樹(使得孩子是父母的子集),這棵樹將和后綴鏈接構(gòu)成的樹相同。

?

證明.終點(diǎn)集合能構(gòu)成一棵樹這一事實(shí)由引理2得出(兩個(gè)終點(diǎn)集合要么不相交,要么一個(gè)包含另一個(gè))。

?

我們現(xiàn)在考慮任意狀態(tài)v≠t_0,及其后綴鏈接link(v)。根據(jù)后綴鏈接的定義和引理2得出:

endpos(v)?endpos(link(v))

這和上一引理證明了我們的斷言:后綴鏈接樹和終點(diǎn)集合樹相同。

?

這里是一個(gè)后綴鏈接的例子,表示字符串"abcbc"




小結(jié)

在學(xué)習(xí)具體算法之前,總結(jié)上面積累的知識(shí),并引入兩個(gè)輔助符號(hào)。

?

·????????s的所有子串可以按照它們的終點(diǎn)集合被分成等價(jià)類。

·????????后綴自動(dòng)機(jī)由一個(gè)初始狀態(tài)t_0和所有不同的終點(diǎn)等價(jià)類所對應(yīng)的狀態(tài)組成。

·????????每個(gè)狀態(tài)v對應(yīng)一個(gè)或多個(gè)字符串,我們記longest(v)是其中最長者,len(v)是其長度。我們記shortest(v)是這些字符串中

的最短者,其長度為minlen(v)。

·????????該狀態(tài)對應(yīng)的所有字符串是longest(v)的不同后綴,并且包括[minlen(v),len(v)]之間的所有長度。

·????????對每個(gè)狀態(tài)v≠t_0定義的后綴鏈接指向的狀態(tài)對應(yīng)longest(v)的長度為minlen(v)-1的后綴。后綴鏈接形成一棵以t_0為根的

樹,而這棵樹事實(shí)上是所有終點(diǎn)集合的樹狀包含關(guān)系。minlen(v)和link(v)的關(guān)系表示如下:minlen(v)=len(link(v))+1.

·????????如果我們從任意節(jié)點(diǎn)v_0開始沿后綴鏈接移動(dòng),我們早晚會(huì)到達(dá)初始狀態(tài)t_0.在此情況下,我們得到了一系列不相交的區(qū)

間[minlen(v_i),len(v_i)],其并集是一個(gè)連續(xù)區(qū)間。

?


一個(gè)構(gòu)建后綴自動(dòng)機(jī)的線性時(shí)間算法

我們下面描述這個(gè)算法。算法是在線的,即,逐個(gè)向s中加入字符,并適當(dāng)?shù)貙Ξ?dāng)前的自動(dòng)機(jī)進(jìn)行修改。

?為了達(dá)到線性空間的目的,我們將只存儲(chǔ)每個(gè)狀態(tài)的len,link的值,以及轉(zhuǎn)移列表。我們并不支持標(biāo)記終止?fàn)顟B(tài)(我們將

展示如果需要,如何在后綴自動(dòng)機(jī)構(gòu)建完畢后加上這些標(biāo)記)。

?最初自動(dòng)機(jī)由一個(gè)狀態(tài)t_0組成,我們稱之為0狀態(tài)(其余狀態(tài)將被稱作1,2,...)。對此狀態(tài),令len=0,為方便起見,將link

值設(shè)為-1(指向一個(gè)空狀態(tài))。

?因此,現(xiàn)在的任務(wù)就變成了實(shí)現(xiàn)向當(dāng)前字符串末尾添加一個(gè)字符c的操作。

下面我們描述這一操作:


·???????1.?令last為對應(yīng)整個(gè)字符串的狀態(tài)(最初last=0,在每次字符添加操作后我們都會(huì)改變last的值)。

·????????2.建立一個(gè)新的狀態(tài)cur,令len(cur)=len(last)+1,而link(cur)的值并不確定。

·???????3.?我們最初在last,如果它沒有字符c的轉(zhuǎn)移,那就添加字符c的轉(zhuǎn)移,指向cur,然后走向其后綴鏈接,再次檢查——如果沒

有字符c的轉(zhuǎn)移,就添加上去。如果在某個(gè)節(jié)點(diǎn)已有字符c的轉(zhuǎn)移,就停止,并且令p為這個(gè)狀態(tài)的編號(hào)。

·????????4.如果“某節(jié)點(diǎn)已有字符c的轉(zhuǎn)移”這一事件從未發(fā)生,而我們來到了空狀態(tài)-1(經(jīng)由t_0的后綴指針前來),我們簡單地令link(cur)=0,

跳出。

·????????5.假設(shè)我們停在了某一狀態(tài)q,是從某一個(gè)狀態(tài)p經(jīng)字符c的轉(zhuǎn)移而來。現(xiàn)在有兩種情況:len(p)+1=len(q)或不然。

·????????6.如果len(p)+1=len(q),那么我們簡單地令link(cur)=q,跳出。

·????????7.否則,情況就變得更加復(fù)雜。必須新建一個(gè)q的“拷貝”狀態(tài):建立一個(gè)新的狀態(tài)clone,將q的數(shù)據(jù)拷貝給它(后綴鏈接,以及

轉(zhuǎn)移),除了len的值:需要令len(clone)=len(p)+1.

·????????8.在拷貝之后,我們將cur的后綴鏈接指向clone,并將q的后綴鏈接重定向到clone。

·????????9.最終,我們需要做的最后一件事情就是——從p開始沿著后綴鏈接走,對每個(gè)狀態(tài)我們都檢查是否有指向q的,字符c的轉(zhuǎn)移,

如果有就將其重定向至clone(如果沒有,就終止循環(huán))。

·????????10.在任何情況下,無論在何處終止了這次添加操作,我們最后都將更新last的值,將其賦值為cur。

如果我們還需要知道哪些節(jié)點(diǎn)是終止節(jié)點(diǎn)而哪些不是,我們可以在構(gòu)建整個(gè)字符串的后綴自動(dòng)機(jī)之后找出所有終止節(jié)點(diǎn)。對此我們

考慮對應(yīng)整個(gè)字符串的節(jié)點(diǎn)(顯然,就是我們儲(chǔ)存在變量last中的節(jié)點(diǎn)),我們沿著它的后綴鏈接走,直到到達(dá)初始狀態(tài),并且將

途徑的每個(gè)節(jié)點(diǎn)標(biāo)記為終止節(jié)點(diǎn)。很好理解,如此我們標(biāo)記了字符串s所有后綴的對應(yīng)狀態(tài),也就是我們想要找出的終止?fàn)顟B(tài)。

?

在下一節(jié)中我們從細(xì)節(jié)上考慮算法的每一步,并證明其正確性。

這里我們僅注意一點(diǎn):每個(gè)字符的添加會(huì)導(dǎo)致向自動(dòng)機(jī)中添加一個(gè)或兩個(gè)狀態(tài)。因此,狀態(tài)數(shù)顯然是線性的。?

轉(zhuǎn)移數(shù)量的線性性,以及算法的線性時(shí)間復(fù)雜度較難理解,它們將在下面被證明,位于算法正確性的證明之后。



算法的正確性證明

·????????我們稱轉(zhuǎn)移(p,q)是連續(xù)的,如果len(p)+1=len(q)。否則,即len(p)+1<len(q)時(shí),我們稱之為不連續(xù)轉(zhuǎn)移。

·????????正如在算法描述中可以看到的那樣,連續(xù)轉(zhuǎn)移和不連續(xù)轉(zhuǎn)移導(dǎo)致了算法流程的不同分支。連續(xù)轉(zhuǎn)移)被如此命名是因?yàn)?#xff0c;自第

一次出現(xiàn)后,它們將保持不變。相反,不連續(xù)轉(zhuǎn)移可能會(huì)在向字符串中添加新字符的過程中被改變(可能會(huì)改變該邊指向的狀態(tài))。

·????????為了避免歧義,我們稱s是我們已經(jīng)構(gòu)建了自動(dòng)機(jī)的字符串,它正準(zhǔn)備添加當(dāng)前字符c。

·????????算法開始時(shí)我們創(chuàng)建了新狀態(tài)cur,它將匹配整個(gè)字符串s+c。我們之所以必須新建一個(gè)狀態(tài)的原因是顯然的——在添加新字

符后,出現(xiàn)了一個(gè)新的終點(diǎn)等價(jià)類——一類以新字符串s+c的末尾為結(jié)尾的子串。

·????????在創(chuàng)建新狀態(tài)后,算法從和整個(gè)字符串s匹配的狀態(tài)開始,沿著后綴鏈接移動(dòng),在途中試圖添加指向cur的,字符c的轉(zhuǎn)移。但

我們只會(huì)在不和已存在轉(zhuǎn)移沖突的情況下添加新的轉(zhuǎn)移,因此一旦我們遇到了一個(gè)字符c的轉(zhuǎn)移,我們就必須立刻停止。

·????????最簡單的情形——如果我們來到了空狀態(tài)-1,向途中所有節(jié)點(diǎn)添加了字符c的轉(zhuǎn)移。這就意味著字符c在字符串s中先前未曾出

現(xiàn)。我們成功地添加了所有的轉(zhuǎn)移,只需要記下狀態(tài)cur的后綴鏈接——它顯然必須等于0,因?yàn)檫@種情況下cur匹配字符串s+c的一

切后綴。

·????????第二種情況——當(dāng)我們進(jìn)入一個(gè)已存在的轉(zhuǎn)移(p,q)時(shí)。這意味著我們試圖向字符串中添加字符x+c(其中x是字符串s的某一后

綴,長度為len(p)),且該字符串先前已經(jīng)被加入了自動(dòng)機(jī)(即,字符串x+c已經(jīng)作為子串包含在字符串s中)。因?yàn)槲覀兗僭O(shè)字符

串s的自動(dòng)機(jī)已被正確構(gòu)建,我們并不應(yīng)該添加新的轉(zhuǎn)移。
然而,cur的后綴鏈接指向哪里有一定復(fù)雜性。我們需要將后綴鏈接指向一個(gè)長度恰好和x+c相等的狀態(tài),即,該狀態(tài)的len值必

須等于len(p)+1.但這樣一種情況可能并不存在:在此情況下我們必須添加一個(gè)“分割的”狀態(tài)。

·????????因此,一種可能的情形是,轉(zhuǎn)移(p,q)變得連續(xù),即,len(q)=len(p)+1.在這種情況下,事情變得簡單,不必再進(jìn)行任何分割,

我們只需要將cur的后綴鏈接指向q。

·????????另一種更復(fù)雜的情況——當(dāng)轉(zhuǎn)移不連續(xù)時(shí),即len(q)>len(p)+1.這意味著狀態(tài)q不僅僅匹配對我們必須的,長度len(p)+1的子串

w+c,它還匹配一個(gè)更長的子串。我們不得不新建一個(gè)“分割的”狀態(tài)q:將子串分割成兩段,第一段將恰在長度len(p)+1處結(jié)束。

如何實(shí)現(xiàn)這個(gè)“分割”呢?我們“拷貝”一個(gè)狀態(tài)q,將其復(fù)制為clone,但參數(shù)len(clone)=len(p)+1.我們將q的所有轉(zhuǎn)移復(fù)制給clone,

因?yàn)闊o論如何我們不想改變經(jīng)過p的路徑。從clone出發(fā)的后綴鏈接總是指向q原先的后綴鏈接,而且q的后綴鏈接將指向clone。

在拷貝之后,我們將cur的后綴鏈接指向clone——我們拷貝它就是為了干這個(gè)的。

?最后一步——重定向一些指向q的轉(zhuǎn)移,將它們改為指向clone。哪些轉(zhuǎn)移必須被重定向?只需要重定向那些匹配所有w+c的后

綴的。即,我們需要持續(xù)沿著后綴鏈接移動(dòng),從p開始,只要沒有到達(dá)空狀態(tài)-1或者沒有到達(dá)一個(gè)狀態(tài),其c的轉(zhuǎn)移指向不同于q的

狀態(tài)。

?


證明操作個(gè)數(shù)是線性的


首先,我們曾經(jīng)說過要保證字母表的大小是常數(shù)。否則,那么線性時(shí)間就不再成立:從一個(gè)頂點(diǎn)出發(fā)的轉(zhuǎn)移被儲(chǔ)存在B-樹中,

它支持按值的快速查找和添加操作。因此,如果我們記字母表的大小是k,算法的漸進(jìn)復(fù)雜度將是O(n*logk),空間復(fù)雜度O(n)。但

是,如果字母表足夠小,就有可能犧牲部分空間,不采用平衡樹,而對每個(gè)節(jié)點(diǎn)用一個(gè)長度為k的數(shù)組(支持按值的快速查找)和一

個(gè)動(dòng)態(tài)鏈表(支持快速遍歷所有存在的鍵值)儲(chǔ)存轉(zhuǎn)移。這樣O(n)的算法就能夠運(yùn)行,但需要消耗O(nk)的空間。

因此,我們假設(shè)字母表的大小是常數(shù),即,每個(gè)按字符查詢轉(zhuǎn)移的操作、添加轉(zhuǎn)移、尋找下一個(gè)轉(zhuǎn)移——所有這些操作我們都

認(rèn)為是O(1)的。

?如果我們觀察算法的所有部分,會(huì)發(fā)現(xiàn)其中三處的線性時(shí)間復(fù)雜度并不顯然:

?

·????????第一處:從last狀態(tài)開始,沿著后綴鏈接移動(dòng),并且添加字符c的轉(zhuǎn)移。

·????????第二處:將q復(fù)制給新狀態(tài)clone時(shí)復(fù)制轉(zhuǎn)移。

·????????第三處:將指向q的轉(zhuǎn)移重定向到clone。

我們使用眾所周知的事實(shí):后綴自動(dòng)機(jī)的大小(狀態(tài)和轉(zhuǎn)移的數(shù)目)是線性的。(對狀態(tài)個(gè)數(shù)是線性的證明來自算法本身,對于

轉(zhuǎn)移個(gè)數(shù)是線性的證明,我們將在下面給出,在實(shí)現(xiàn)算法之后。)。

? 那么顯然第一處和第二處是漸進(jìn)線性的,因?yàn)槊看尾僮鞫紩?huì)增加新的狀態(tài)和轉(zhuǎn)移。

? 仍然需要估算第三處總的線性復(fù)雜度——在每次添加字符時(shí)我們將指向q的轉(zhuǎn)移重定向至clone。

?我們不妨關(guān)注shortest(link(last))。注意到,在沿著后綴鏈接上溯的過程中,當(dāng)前節(jié)點(diǎn)的shortest的長度總是嚴(yán)格變小。

?顯然,在向s中添加新字符之前,shortest(link(last))的長度不小于shortest(p)的長度,因?yàn)閘ink(last)至多是p。爾后假設(shè)我們由q

拷貝得到了節(jié)點(diǎn)clone,并試圖從p沿后綴鏈接上溯,將所有通往q的轉(zhuǎn)移重定向?yàn)橥ㄍ鵦lone。設(shè)v是shortest(當(dāng)前節(jié)點(diǎn)),在clone剛

剛建立完成后,v=short(p)。然后,在每次沿后綴鏈接上溯時(shí),v的值都會(huì)變小,而如果當(dāng)前節(jié)點(diǎn)存在經(jīng)過字符c通往q的轉(zhuǎn)移,就意

味著q對應(yīng)的字符串集合中包含v+c,也意味著clone包含的字符串集合中包含v+c。換言之,我們?yōu)閏lone包含的字符串集合找到了一

個(gè)更短的元素,即減少了short(clone)的長度。

在“向s中添加新字符”的整個(gè)流程結(jié)束后,有l(wèi)ink(last)=link(cur)=clone。根據(jù)上面的討論,新的shortest(link(last))的長度變小(或

保持不變),而且這一長度減小的值和上溯的操作數(shù)同階。

?綜上,shortest(link(last))作為s一個(gè)后綴的起始位置在整個(gè)過程中不斷右移,而且每次沿后綴指針上溯都會(huì)導(dǎo)致該位置嚴(yán)格右移。

由于在程序結(jié)束時(shí)這一起始位置不超過n,所以這一過程的時(shí)間復(fù)雜度是線性的。

?雖然沒什么用,但同樣的討論可以被用來證明第一處的線性性,以代替對狀態(tài)個(gè)數(shù)線性性的證明。)

?


算法的實(shí)現(xiàn)

首先我們描述一個(gè)數(shù)據(jù)結(jié)構(gòu),它儲(chǔ)存特定的一段信息(len,link,轉(zhuǎn)移列表)。如有必要,你可以增加表示終止?fàn)顟B(tài)的標(biāo)簽,以及其他需要

的信息。

我們用STL容器map存儲(chǔ)轉(zhuǎn)移列表,其空間復(fù)雜度為O(n),而處理整個(gè)字符串的時(shí)間復(fù)雜度為O(n*logk)。

[cpp]?view plaincopy
  1. struct?state?{??
  2. ????int?len,link;??
  3. ????map<char,int>?next;??
  4. };??

后綴自動(dòng)機(jī)本身將被儲(chǔ)存在一個(gè)state類型的數(shù)組中。正如下一節(jié)中將證明的那樣,如果程序中所處理字符串的最大可能長度是MAXN,

那么至多會(huì)占用2*MAXN-1個(gè)狀態(tài)。同時(shí),我們儲(chǔ)存變量last——當(dāng)前匹配整個(gè)字符串的狀態(tài)。

[cpp]?view plaincopy
  1. const?int?MAXLEN?=?100000;??
  2. state?st[MAXLEN*2];??
  3. int?sz,?last;??

我們給出初始化后綴自動(dòng)機(jī)的函數(shù)(新建一個(gè)初始狀態(tài)):

[cpp]?view plaincopy
  1. void?sa_init()?{??
  2. ???sz?=?last?=?0;??
  3. ???st[0].len?=?0;??
  4. ???st[0].link?=?-1;??
  5. ???++sz;??
  6. ????/*?
  7. ????//?若關(guān)于不同的字符串多次建立后綴自動(dòng)機(jī),就需要執(zhí)行這些代碼:?
  8. ????for?(int?i=0;?i<MAXLEN*2;?++i)?
  9. ????????st[i].next.clear();?
  10. ????*/??
  11. }??


最后,我們給出基礎(chǔ)函數(shù)的實(shí)現(xiàn)——向當(dāng)前字符串的尾部添加一個(gè)字符,并相應(yīng)地修改后綴自動(dòng)機(jī):

[cpp]?view plaincopy
  1. void?sa_extend?(char?c)?{??
  2. ????int?cur?=?sz++;??
  3. ????st[cur].len?=?st[last].len?+?1;??
  4. ????int?p;??
  5. ????for?(p=last;?p!=-1?&&?!st[p].next.count(c);?p=st[p].link)??
  6. ????????st[p].next[c]?=?cur;??
  7. ????if?(p?==?-1)??
  8. ????????st[cur].link?=?0;??
  9. ????else?{??
  10. ????????int?q?=?st[p].next[c];??
  11. ????????if?(st[p].len?+?1?==?st[q].len)??
  12. ????????st[cur].link?=?q;??
  13. ????????else?{??
  14. ????????int?clone?=?sz++;??
  15. ????????????st[clone].len?=?st[p].len?+?1;??
  16. ????????????st[clone].next?=?st[q].next;??
  17. ????????????st[clone].link?=?st[q].link;??
  18. ????????????for?(;?p!=-1?&&?st[p].next[c]==q;?p=st[p].link)??
  19. ????????????st[p].next[c]?=?clone;??
  20. ????????????st[q].link?=?st[cur].link?=?clone;??
  21. ????????}??
  22. ????}??
  23. ????last?=?cur;??
  24. }??


像前面提到的那樣,如果犧牲部分空間(空間復(fù)雜度增至O(nk),其中k是字母表大小),就能夠?qū)θ魏蝛實(shí)現(xiàn)O(n)構(gòu)建自動(dòng)機(jī)——但這將

會(huì)在每個(gè)狀態(tài)中建立一個(gè)長度為k的數(shù)組(用于快速按字符查詢轉(zhuǎn)移)和一個(gè)轉(zhuǎn)移鏈表(用于快速遍歷或者復(fù)制所有轉(zhuǎn)移)。

后綴自動(dòng)機(jī)的其他性質(zhì)

狀態(tài)的數(shù)量

由長度為n的字符串s建立的后綴自動(dòng)機(jī)的狀態(tài)個(gè)數(shù)不超過2n-1(對于n>=3)。

上面描述的算法證明了這一性質(zhì)(最初自動(dòng)機(jī)包含一個(gè)初始節(jié)點(diǎn),第一步和第二步都會(huì)添加一個(gè)狀態(tài),余下的n-2步每步至多由于需要分割,增加兩個(gè)狀態(tài))。

不過,即使不涉及算法,這一性質(zhì)也容易證明。注意到狀態(tài)個(gè)數(shù)等于不同的終點(diǎn)集合的個(gè)數(shù)。此外,終點(diǎn)集合按“子女是父節(jié)點(diǎn)的不同子集”這一原則構(gòu)成一棵樹。

考慮這棵樹,將其稍作擴(kuò)充:若一個(gè)內(nèi)部節(jié)點(diǎn)只有一個(gè)兒子,那就意味著該兒子的終點(diǎn)集合不包含其父親終點(diǎn)集合中的至少一個(gè)值;那么我們就創(chuàng)建一個(gè)虛擬節(jié)點(diǎn),

其終點(diǎn)集合為這個(gè)值。最終,我們得到了一棵樹,其內(nèi)部節(jié)點(diǎn)度數(shù)均>1,而葉子節(jié)點(diǎn)個(gè)數(shù)不超過n。因此,這棵樹的結(jié)點(diǎn)個(gè)數(shù)不超過2n-1.自然,原樹的結(jié)點(diǎn)個(gè)數(shù)也

不超過2n-1.

這樣我們就獨(dú)立于算法地證明了這一性質(zhì)。

有趣的是,這一上限無法被改善,即存在達(dá)到這一上限的例子:?

"abbbb..."

從第三次開始,每次添加字符時(shí)都會(huì)進(jìn)行分割,因此結(jié)點(diǎn)個(gè)數(shù)將達(dá)到2n-1.


轉(zhuǎn)移的數(shù)量

由長度為n的字符串s建立的后綴自動(dòng)機(jī)中,轉(zhuǎn)移的數(shù)量不超過3n-4(對于n>=3)。

?

證明.

?我們計(jì)算“連續(xù)的”轉(zhuǎn)移個(gè)數(shù)。考慮以t_0為初始節(jié)點(diǎn)的自動(dòng)機(jī)的最長路徑樹。這棵樹將包含所有連續(xù)的轉(zhuǎn)移,樹的邊數(shù)比結(jié)點(diǎn)個(gè)數(shù)小1,這意

味著連續(xù)的轉(zhuǎn)移個(gè)數(shù)不超過2n-2.

? ?我們再來計(jì)算不連續(xù)的轉(zhuǎn)移個(gè)數(shù)。考慮每個(gè)不連續(xù)轉(zhuǎn)移;假設(shè)該轉(zhuǎn)移——轉(zhuǎn)移(p,q),標(biāo)記為c。對自動(dòng)機(jī)運(yùn)行一個(gè)合適的字符串u+c+w,其

中字符串u表示從初始狀態(tài)到p經(jīng)過的最長路徑,w表示從q到任意終止節(jié)點(diǎn)經(jīng)過的最長路徑。一方面,對所有不連續(xù)轉(zhuǎn)移,字符串u+c+w都是不同

的(因?yàn)樽址畊和w僅包含連續(xù)轉(zhuǎn)移)。另一方面,每個(gè)這樣的字符串u+c+w,由于在終止?fàn)顟B(tài)結(jié)束,它必然是完整串s的一個(gè)后綴。由于s的非

空后綴僅有n個(gè),并且完整串s不能是某個(gè)u+c+w(因?yàn)橥暾畇匹配一條包含n個(gè)連續(xù)轉(zhuǎn)移的路徑),那么不連續(xù)轉(zhuǎn)移的總共個(gè)數(shù)不超過n-1.

? ?將這兩個(gè)限制加起來,我們就得到了總數(shù)限制3n-3.注意到雖然狀態(tài)個(gè)數(shù)限制可以被數(shù)據(jù)"abbbb..."達(dá)到,但這個(gè)數(shù)據(jù)并未達(dá)到3n-3的轉(zhuǎn)移個(gè)數(shù)

上限。它的轉(zhuǎn)移個(gè)數(shù)是3n-4,符合要求。

? ?有趣的是,仍然存在達(dá)到轉(zhuǎn)移個(gè)數(shù)上限的數(shù)據(jù):

"abbb...bbbc"


與后綴樹的聯(lián)系,在后綴自動(dòng)機(jī)上建立后綴樹及反之

? ?我們證明兩個(gè)定理,它們能說明后綴樹和后綴自動(dòng)機(jī)之間的相互關(guān)系。

首先我們假定輸入字符串的每個(gè)后綴都在其后綴樹中對應(yīng)一個(gè)節(jié)點(diǎn)(對于任意字符串而言并不一定成立:例如,對于字符串 "aaa...")。在后綴

樹的典型實(shí)現(xiàn)中,我們通過在字符串的末尾加上一個(gè)特殊符號(hào)(例如"#"或"$")來保證這一點(diǎn)。

? ?方便起見,我們引入如下記號(hào):rev(s)——將字符串s反過來寫,DAWG(s)——這是由字符串s建立的后綴自動(dòng)機(jī),ST(s)——這是s的后綴樹。

? ?我們介紹“擴(kuò)展指針”的概念:對于樹節(jié)點(diǎn)v和字符c,ext[c,v]指向樹中對應(yīng)于字符串c+v的節(jié)點(diǎn)(如果路徑c+v在某邊的終點(diǎn)結(jié)束,那就將其指向該

邊的較低點(diǎn));如果這樣一條路徑c+v不在樹中,那么擴(kuò)展指針未定義。在某種意義上,擴(kuò)展指針的對立面就是后綴鏈接。

?

定理1.DAWG(s)中后綴鏈接組成的樹就是后綴樹ST(rev(s))

?

定理2.圖DAWG(s)的邊都能用后綴樹ST(rev(s))的擴(kuò)展指針表示。另外,DAWG(s)中的連續(xù)轉(zhuǎn)移就是ST(rev(s))中反向的后綴指針。

?

這兩條定理允許使用兩個(gè)數(shù)據(jù)結(jié)構(gòu)之一在O(n)的時(shí)間內(nèi)構(gòu)建另外一個(gè)——這兩個(gè)簡單的算法將在下面定理3,4討論。

? ?出于說明需要,我們下面展示一個(gè)包含后綴鏈接的后綴自動(dòng)機(jī)的例子,以及其倒序字符串的相應(yīng)后綴樹。例如,令字符

串s="abcbc"

? ?DAWG("abcbc")(出于簡便我們在每個(gè)狀態(tài)上標(biāo)出其識(shí)別的最長串):




ST("cbcba")


引理.

對任意兩個(gè)子串u和w,如下三個(gè)陳述是等價(jià)的:

·????????在字符串s中endpos(u)=endpos(w)

·????????在字符串rev(s)中firstpos(rev(u))=firstpos(rev(w))

·????????在后綴樹ST(rev(s))中,rev(u)和rev(w)匹配從根開始的一段相同路徑。


證明十分顯然:如果兩個(gè)字符串的起始位置集合相同,那么一個(gè)字符串只作為另外一個(gè)的前綴出現(xiàn),這意味著在后綴樹中,二者之

間并沒有其他節(jié)點(diǎn)。

?因此,后綴自動(dòng)機(jī)中的狀態(tài)和后綴樹中的節(jié)點(diǎn)一一對應(yīng)。

?

定理1的證明.

?后綴自動(dòng)機(jī)中的狀態(tài)和后綴樹中的節(jié)點(diǎn)一一對應(yīng).

?

考慮任意后綴鏈接y=link(x)。根據(jù)后綴鏈接的定義,longest(y)是longest(x)的一個(gè)后綴,并且y是所有滿足條件(和x的終點(diǎn)集合不同)

的狀態(tài)中使len(y)最大者。

?在rev(s)中,這意味著link(x)指向x所對應(yīng)字符串的最長前綴,該前綴對應(yīng)一個(gè)不同的狀態(tài)y。換句話說,后綴鏈接link(x)指向后綴樹中節(jié)

點(diǎn)x的父親。

?

定理2的證明.

?

后綴自動(dòng)機(jī)中的狀態(tài)和后綴樹中的節(jié)點(diǎn)一一對應(yīng)。

?

考慮后綴自動(dòng)機(jī)DAWG(s)中的任意轉(zhuǎn)移(x,y,c)。這意味著y是包含子串longest(x)+c的終點(diǎn)集合等價(jià)類。對于rev(s),y對應(yīng)了一個(gè)子串,

該子串的firstpos(在文本rev(s)中)和c+rev(longest(x))的firstpos相同。

?這意味著:

rev(longest(y))=ext[c,rev(longest(x))].

(注:這里的用法并不嚴(yán)謹(jǐn)……請自行把字符串和節(jié)點(diǎn)對應(yīng))?

也就是該定理的第一部分,我們還需要證明第二部分:自動(dòng)機(jī)中的所有連續(xù)轉(zhuǎn)移對應(yīng)樹中的后綴指針。對于連續(xù)轉(zhuǎn)移,有l(wèi)ength(y)=length(x)+1,

即在標(biāo)識(shí)字符c后我們到達(dá)了一個(gè)狀態(tài),它是一個(gè)不同的等價(jià)類。這意味著在rev(s)的后綴樹中,x節(jié)點(diǎn)對應(yīng)的字符串恰好是y節(jié)點(diǎn)所對應(yīng)字符串的,長

度比它小1的后綴——也就是說,后綴樹中y的后綴指針指向x,(x,y)就是樹中的反向后綴指針。

?

定理得證。

?

定理3.

使用后綴自動(dòng)機(jī)DAWG(s),我們可以用O(n)的時(shí)間構(gòu)建后綴樹ST(rev(s))。

?

定理4.

使用后綴樹ST(rev(s)),我們可以用O(n)的時(shí)間構(gòu)建后綴自動(dòng)機(jī)DAWG(s)。

?

定理3的證明.

后綴樹ST(rev(s))的節(jié)點(diǎn)和DAWG(s)中的狀態(tài)一一對應(yīng)。樹中與自動(dòng)機(jī)中狀態(tài)v相對應(yīng)的節(jié)點(diǎn)表示一個(gè)長度為len(v)的字符串。


根據(jù)定理1,ST(rev(s))中的邊恰好是把DAWG(s)的后綴鏈接反向,而邊的標(biāo)記可以借助不同狀態(tài)的len計(jì)算(譯者注:從葉子開始,利用自動(dòng)

機(jī)中狀態(tài)的len值計(jì)算后綴樹中的節(jié)點(diǎn)對應(yīng)于哪個(gè)子串),或者更方便地,對自動(dòng)機(jī)中每個(gè)狀態(tài)我們都能知道其endpos集合中的一個(gè)元素(在構(gòu)建

后綴自動(dòng)機(jī)時(shí)維護(hù))。

?至于樹中的后綴指針,我們可以基于定理2構(gòu)建:查找自動(dòng)機(jī)中所有的連續(xù)轉(zhuǎn)移,對所有這樣的轉(zhuǎn)移(x,y)我們都在樹中添加一個(gè)后綴指針link[y]=x。

?因此,在O(n)時(shí)間內(nèi)我們就可以構(gòu)建一棵后綴樹及其中的后綴指針。

?(如果我們認(rèn)為字母表的大小k并非常數(shù),那么重建操作將花費(fèi)O(n*logk)的時(shí)間。)

?

定理4的證明.

后綴自動(dòng)機(jī)DAWG(s)包含的狀態(tài)和ST(rev(s))中的節(jié)點(diǎn)一一對應(yīng)。對每個(gè)狀態(tài)v,其對應(yīng)的最長字符串longest(v)都和后綴樹中從根到v的路徑翻轉(zhuǎn)后

形成的字符串相同。

?

根據(jù)定理2,為了構(gòu)建自動(dòng)機(jī)中的所有轉(zhuǎn)移,我們需要找到所有擴(kuò)展ext[c,v]的指針。

?首先,注意到其中的一些指針直接由樹中的后綴指針得到。事實(shí)上,如果對于樹中任意節(jié)點(diǎn)x,我們考慮其后綴指針y=link[x],那就意味著自動(dòng)機(jī)中

有一個(gè)從y指向x的連續(xù)轉(zhuǎn)移,標(biāo)記為樹節(jié)點(diǎn)x所對應(yīng)字符串的第一個(gè)字符。

?不過,只是這樣我們并不能找到所有的擴(kuò)展。額外地,有必要從葉子到根遍歷后綴樹,而且對于每個(gè)節(jié)點(diǎn)v都遍歷其所有兒子,對每個(gè)兒子觀察所有

擴(kuò)展指針ext[c,w],如果該指針上的字符c在節(jié)點(diǎn)v中還未發(fā)現(xiàn),就將其復(fù)制到v中:

ext[c,v]=ext[c,w],如果ext[c,w]=nil.

這一過程將在O(n)時(shí)間內(nèi)完成,如果我們認(rèn)為字母表的大小是常數(shù)。

最終,還需要建立后綴自動(dòng)機(jī)中的后綴鏈接。而根據(jù)定理1,后綴鏈接可以直接由后綴樹ST(rev(s))的邊獲得。

這樣,我們就得到使用從倒序字符串的后綴樹建立后綴指針的O(n)算法。

?(不過,若字母表的大小k是變量,那么漸進(jìn)復(fù)雜度就是O(n*logk))。

?


在解決問題中的應(yīng)用

下面看在后綴自動(dòng)機(jī)的幫助下我們能做什么。

?

簡便起見,我們假設(shè)字母表的大小k為常數(shù)。

?


存在性查詢


問題.給定文本T,詢問格式如下:給定字符串P,問P是否是T的子串。?

復(fù)雜度要求.預(yù)處理O(length(T)),每次詢問O(P)。

?

算法.我們對文本T用O(length(T))建立后綴自動(dòng)機(jī)。

現(xiàn)在回答單次詢問。假設(shè)狀態(tài)——變量v,最初是初始狀態(tài)T_0.我們沿字符串P給出的路徑走,因此從當(dāng)前狀態(tài)經(jīng)轉(zhuǎn)移來到新的狀態(tài)v

。如果在某時(shí)刻,當(dāng)前狀態(tài)沒有要求字符的轉(zhuǎn)移,那么答案就是"no"。如果我們處理了整個(gè)字符串P,答案就是"yes"。

顯然這一算法將在時(shí)間O(length(P))內(nèi)運(yùn)行完畢。并且,該算法實(shí)際上找出了P在文本中出現(xiàn)過的最長前綴——如果模式串使得這些前綴

都很短,算法將比處理全部模式串要快得多。

?


不同的子串個(gè)數(shù)


問題.給定字符串S,問它有多少不同的子串。

?復(fù)雜度要求.O(length(S))。

?

算法.我們將字符串S建立后綴自動(dòng)機(jī)。

?在后綴自動(dòng)機(jī)中,S的任意子串都對應(yīng)自動(dòng)機(jī)中的一條路徑。答案就是從初始節(jié)點(diǎn)t_0開始,自動(dòng)機(jī)中不同的路徑條數(shù)。

?已知后綴自動(dòng)機(jī)是一張有向無環(huán)圖,我們可以考慮用動(dòng)態(tài)規(guī)劃計(jì)算不同的路徑數(shù)量。

? 也就是,令d[v]為從狀態(tài)v開始的不同路徑條數(shù)(包括長度為零的路徑),則有轉(zhuǎn)移:




即d[v]是v所有后繼節(jié)點(diǎn)的d值之和加上1.

最終答案就是d[t_0]-1(減一以忽略空串)。



不同子串的總長


問題.給定字符串S,求其所有不同子串的總長度。

復(fù)雜度要求.O(length(S)).

?

算法.這一問題的答案和上一題類似,但現(xiàn)在我們必須考慮兩個(gè)狀態(tài):不同子串的個(gè)數(shù)d[v]和它們的總長ans[v].

? ?上一題已描述了d[v]的計(jì)算方法,而ans[v]的計(jì)算方法如下:

即取所有后繼節(jié)點(diǎn)w的ans值,并將它和d[w]相加。因?yàn)檫@是每個(gè)字符串的首字母。

?


字典序第k小子串


問題.給定字符串S,一系列詢問——給出整數(shù)K_i,計(jì)算S的所有子串排序后的第K_i個(gè)。

復(fù)雜度要求.單次詢問O(length(ans)*Alphabet),其中ans是該詢問的答案,Alphabet是字母表大小。


?算法.這一問題的基礎(chǔ)思路和上兩題類似。字典序第k小子串——自動(dòng)機(jī)中字典序第k小的路徑。因此,考慮從每個(gè)狀態(tài)出

發(fā)的不同路徑數(shù),我們將得以輕松地確定第k小路徑,從初始狀態(tài)開始逐位確定答案。

?


最小循環(huán)移位


問題.給定字符串S,找到和它循環(huán)同構(gòu)的字典序最小字符串。

?復(fù)雜度要求.O(length(S)).

?

算法.我們將字符串S+S建立后綴自動(dòng)機(jī)。該自動(dòng)機(jī)將包含和S循環(huán)同構(gòu)的所有字符串。

?從而,問題就簡化成了在自動(dòng)機(jī)中找出字典序最小的,長度為length(S)的路徑,這很簡單:從初始狀態(tài)開始,每一步都貪心地走

,經(jīng)過最小的轉(zhuǎn)移。

?


出現(xiàn)次數(shù)查詢


問題.給定文本T,詢問格式如下:給定字符串P,希望找出P作為子串在文本T中出現(xiàn)了多少次(出現(xiàn)區(qū)間可以相交)。

?復(fù)雜度要求.預(yù)處理O(length(T)),單次詢問O(length(P)).

?

算法.我們將文本T建立后綴自動(dòng)機(jī)。

?然后我們需要進(jìn)行預(yù)處理:對自動(dòng)機(jī)中的每個(gè)狀態(tài)v都計(jì)算cnt[v],等于其endpos(v)集合的大小。事實(shí)上,所有在T中對應(yīng)同一狀態(tài)的

字符串都在T中出現(xiàn)了相同次數(shù),該次數(shù)等于endpos中的位置數(shù)。

?不過,我們無法對所有狀態(tài)明確記錄endpos集合,所以我們只計(jì)算其大小cnt.

?為了實(shí)現(xiàn)這一點(diǎn),如下處理。對每個(gè)狀態(tài),如果它不是由“拷貝”而來,最初就賦值cnt=1.然后我們按長度len降序遍歷所有序列,并將

當(dāng)前的cnt[v]加給后綴鏈接:

?cnt[link(v)]+=cnt[v].

? 你可能會(huì)說我們并沒有對每個(gè)狀態(tài)計(jì)算出了正確的cnt值。

?為什么這是對的?不經(jīng)“拷貝”而來的狀態(tài)恰好有l(wèi)ength(S)個(gè),而且其中的第i個(gè)是我們添加第i個(gè)字符時(shí)得到的。因此,最初這些狀態(tài)的cnt=1,

其他狀態(tài)的cnt=0.

?然后我們對每個(gè)狀態(tài)v執(zhí)行如下操作:cnt[link(v)]+=cnt[v].其意義在于,如果某字符串對應(yīng)狀態(tài)v,曾在cnt[v]中出現(xiàn)過,那么它的所有后綴都

同樣在其中出現(xiàn)。

?這樣,我們就掌握了如何對自動(dòng)機(jī)中所有狀態(tài)計(jì)算cnt值的方法。

?在此之后,詢問的答案就變得平凡——只需要返回cnt[t],其中t是模式串P所對應(yīng)的狀態(tài)。

?


首次出現(xiàn)位置查詢


問題.給定文本T,詢問格式如下:給定字符串P,求P在文本中第一次出現(xiàn)的位置。

?復(fù)雜度要求.預(yù)處理O(length(T)),單次詢問O(length(P)).

?

算法.對文本T建立后綴自動(dòng)機(jī)。

?為了解決這一問題,我們需要預(yù)處理firstpos,找到自動(dòng)機(jī)中所有狀態(tài)的出現(xiàn)位置,即,對每個(gè)狀態(tài)v我們希望找到一個(gè)位置firstpos[v],代表

其第一次出現(xiàn)的位置。換句話說,我們希望預(yù)先找出每個(gè)endpos(v)中的最小元素(我們無法明確記錄整個(gè)endpos集合)。

? 維護(hù)這些firstpos的最簡單方法是在構(gòu)建自動(dòng)機(jī)時(shí)一并計(jì)算,當(dāng)我們創(chuàng)建新的狀態(tài)cur時(shí),一旦進(jìn)入函數(shù)sa_extend(),就確定該值:

?firstpos(cur)=len(cur)-1(如果我們的下標(biāo)從0開始)。

當(dāng)拷貝節(jié)點(diǎn)q時(shí),令:

?firstpos(clone)=firstpos(q),(因?yàn)橹挥幸粋€(gè)別的可能值——firstpos(cur),顯然更大)。

?這樣就得到了查詢的答案——firstpos(t)-length(P)+1,其中t是模式串P對應(yīng)的狀態(tài)。

?


所有出現(xiàn)位置查詢


問題.給定文本T,詢問格式如下:給定字符串P,要求給出P在T中的所有出現(xiàn)位置(出現(xiàn)區(qū)間可以相交)。

復(fù)雜度要求.預(yù)處理O(length(T))。單次詢問O(length(P)+answer(P)),其中answer(P)是答案集合的大小,即,要求時(shí)間復(fù)雜度和輸入輸出同階。

?

算法.對文本T建立后綴自動(dòng)機(jī),和上一個(gè)問題相似,在構(gòu)建自動(dòng)機(jī)的過程中對每個(gè)狀態(tài)計(jì)算第一次出現(xiàn)的終點(diǎn)firstpos。

?

假設(shè)我們收到了一個(gè)詢問——字符串P。我們找到了它對應(yīng)的狀態(tài)t。

?顯然應(yīng)當(dāng)返回firstpos(t)。還有哪些位置?我們考慮自動(dòng)機(jī)中那些包含了字符串P的狀態(tài),即那些P是其后綴的狀態(tài)。

?換言之,我們需要找出所有能通過后綴鏈接到達(dá)狀態(tài)t的狀態(tài)。

?因此,為了解決這一問題,我們需要對每個(gè)節(jié)點(diǎn)儲(chǔ)存指向它的所有后綴鏈接。為了找到答案,我們需要沿著這些翻轉(zhuǎn)的后綴鏈接進(jìn)行DFS/BFS,從狀

態(tài)t開始。

?這一遍歷將在O(answer(P))時(shí)間內(nèi)結(jié)束,因?yàn)槲覀儾粫?huì)訪問同一狀態(tài)兩次(因?yàn)槊總€(gè)狀態(tài)的后綴鏈接僅指向一個(gè)點(diǎn),因此不可能有兩條路徑通往同一

狀態(tài))。

?然而,兩個(gè)狀態(tài)的firstpos值可能會(huì)相同,如果一個(gè)狀態(tài)是由另一個(gè)拷貝而來。但這不會(huì)影響漸進(jìn)復(fù)雜度,因?yàn)槊總€(gè)非拷貝得到的節(jié)點(diǎn)只會(huì)有一個(gè)拷貝。

?此外,你可以輕松地除去那些重復(fù)的位置,如果我們不考慮那些拷貝得來的狀態(tài)的firstpos。事實(shí)上,所有拷貝得來的狀態(tài)都被其“母本”狀態(tài)的后綴鏈接

指向。因此,我們對每個(gè)節(jié)點(diǎn)記錄標(biāo)簽is_clon,我們不考慮那些is_clon=true的狀態(tài)的firstpos。這樣我們就得到了answer(P)個(gè)不重復(fù)地狀態(tài)。

?給出一個(gè)離線版本的實(shí)現(xiàn):

[cpp]?view plaincopy
  1. struct?state?{??
  2. ????...??
  3. ????bool?is_clon;??
  4. ????int?first_pos;??
  5. ????vector<int>?inv_link;??
  6. ????};??
  7. ??????
  8. ???????
  9. ...?后綴自動(dòng)機(jī)構(gòu)建完畢?...??
  10. for?(int?v=1;?v<sz;?++v)??
  11. ????st[st[v].link].inv_link.push_back?(v);??
  12. ...??
  13. ????
  14. ???
  15. //?回答詢問--返回所有的出現(xiàn)位置(出現(xiàn)區(qū)間可能有重疊)??
  16. void?output_all_occurences?(int?v,?int?P_length)?{??
  17. ???if?(!?st[v].is_clon)??
  18. ????????cout?<<?st[v].first_pos?-?P_length?+?1?<<?endl;??
  19. ???for?(size_t?i=0;?i<st[v].inv_link.size();?++i)??
  20. ????????output_all_occurences?(st[v].inv_link[i],?P_length);??
  21. }??


查詢不在文本中出現(xiàn)的最短字符串

問題.給定字符串S和字母表。要求找出一個(gè)長度最短的字符串,使得它不是S的子串。

復(fù)雜度要求.O(length(S)).


?算法.在字符串S的后綴自動(dòng)機(jī)上進(jìn)行動(dòng)態(tài)規(guī)劃。

?令d[v]為節(jié)點(diǎn)v的答案,即,我們已經(jīng)輸入了字符串的一部分,匹配到v,我們希望找出有待添加的最少字符數(shù)量,以到達(dá)一個(gè)不存在的轉(zhuǎn)移。

?計(jì)算d[v]非常簡單。如果在v處某個(gè)轉(zhuǎn)移不存在,那么d[v]=1:用這個(gè)字符來“跳出”自動(dòng)機(jī),以得到所求字符串。

?否則,一個(gè)字符串無法達(dá)到要求,因此我們必須取所有字符中的最小答案:

原問題的答案等于d[t_0],而所求字符串可以用記錄轉(zhuǎn)移路徑的方法得到。

?


求兩個(gè)字符串的最長公共子串


問題.給定兩個(gè)字符串S和T。要求找出它們的最長公共子串,即一個(gè)字符串X,它同時(shí)是S和T的子串。

? ?復(fù)雜度要求.O(length(S)+length(T)).

?

算法.我們對字符串S建立后綴自動(dòng)機(jī)。

?

我們按照字符串T在自動(dòng)機(jī)上走,查找它每個(gè)前綴在S中出現(xiàn)過的最長后綴。換句話說,對字符串T中的每個(gè)位置,我們都想找出S和T在該位置結(jié)束的最長公共子串。

?

為了實(shí)現(xiàn)這一點(diǎn),我們定義兩個(gè)變量:當(dāng)前狀態(tài)v和當(dāng)前長度l。這兩個(gè)變量描述了當(dāng)前的匹配部分:其長度和狀態(tài),對應(yīng)哪個(gè)字符串(如果不儲(chǔ)存長度就無法確定這一點(diǎn),因?yàn)橐粋€(gè)狀態(tài)可能匹配多個(gè)有不同長度的字符串)。

?

最初,p=t_0,l=0,即,匹配部分為空。

?

現(xiàn)在我們考慮字符T[i],我們希望找到這個(gè)位置的答案。

·????????如果自動(dòng)機(jī)中的狀態(tài)v有一個(gè)符號(hào)T[i]的轉(zhuǎn)移,我們可以簡單地走這個(gè)轉(zhuǎn)移,然后將長度l加一。

·????????如果狀態(tài)v沒有該轉(zhuǎn)移,我們應(yīng)當(dāng)嘗試縮短當(dāng)前匹配部分,為此應(yīng)當(dāng)沿著后綴鏈接走:
v=link(v).
在此情況下,當(dāng)前匹配長度必須被減少,但留下的部分盡可能多。顯然,應(yīng)令l=len(v):
l=len(v).
若新到達(dá)的狀態(tài)仍然沒有我們想要的轉(zhuǎn)移,那我們必須再次沿著后綴鏈接走,并且減少l的值,直到我們找到一個(gè)轉(zhuǎn)移(那就返回第一步),或者我們最終到達(dá)了空狀態(tài)-1(這意味著字符T[i]并未在S中出現(xiàn),所以令v=l=0然后繼續(xù)處理下一個(gè)i)。

原問題的答案就是l曾經(jīng)達(dá)到的最大值。

?

這種遍歷方法的漸進(jìn)復(fù)雜度是O(length(T)),因?yàn)樵谝淮我苿?dòng)中我們要么將l加一,要么沿著后綴鏈接走了若干次,每次都會(huì)嚴(yán)格減少l。因此,l總共的減少值之和不可能超過length(T),這意味著線性時(shí)間復(fù)雜度。

?

代碼實(shí)現(xiàn):


[cpp]?view plaincopy
  1. string?lcs?(string?s,?string?t)?{??
  2. ????sa_init();??
  3. ????for?(int?i=0;?i<(int)s.length();?++i)??
  4. ????????sa_extend?(s[i]);??
  5. ??????
  6. ????int?v?=?0,??l?=?0,??
  7. ????????best?=?0,??bestpos?=?0;??
  8. ????for?(int?i=0;?i<(int)t.length();?++i)?{??
  9. ????????while?(v?&&?!?st[v].next.count(t[i]))?{??
  10. ????????????v?=?st[v].link;??
  11. ????????????l?=?st[v].length;??
  12. ????????}??
  13. ????????if?(st[v].next.count(t[i]))?{??
  14. ????????v?=?st[v].next[t[i]];??
  15. ?????????++l;??
  16. ????????}??
  17. ????????if?(l?>?best)??
  18. ????????????best?=?l,??bestpos?=?i;??
  19. ????}??
  20. ????return?t.substr?(bestpos-best+1,?best);??
  21. }??


多個(gè)字符串的最長公共子串


問題.給出K個(gè)字符串S_1~S_K。要求找出它們的最長公共子串,即一個(gè)字符串X,它是所有S_i的子串。

? ?復(fù)雜度要求.O(∑length(S_I)*K).

?

算法.將所有S_i連接在一起成為一個(gè)新的字符串T,其中每個(gè)S_i后要加上一個(gè)不同的分隔符D_i(即加上K個(gè)額外的不同特殊字符D_1~D_K):

我們對字符串T構(gòu)建后綴自動(dòng)機(jī)。

?現(xiàn)在我們需要在后綴自動(dòng)機(jī)找出一個(gè)字符串,它是所有字符串S_i的子串。注意到如果一個(gè)子串在某個(gè)字符串S_j中出現(xiàn)過,那么后綴自動(dòng)機(jī)中存在一

條以這個(gè)子串為前綴的路徑,包含分隔符D_j,但不包含其他分隔符D_1,...,D_j-1,D_j+1,...,D_k。

?因此,我們需要計(jì)算“可達(dá)性”:對自動(dòng)機(jī)中的每個(gè)狀態(tài)和每個(gè)字符D_i,計(jì)算是否有一條從該狀態(tài)開始的路徑,包含分隔符D_i,但不包含別的分隔符。

很容易用DFS/BFS或者動(dòng)態(tài)規(guī)劃實(shí)現(xiàn)。在此之后,原問題的答案就是字符串longest(v),其中v能夠到達(dá)所有的分隔符。

?


OJ上的題目

可以用后綴自動(dòng)機(jī)解決的題目:

SPOJ#7258 SUBLEX"Lexicographical Substring Search"

BZOJ#2555 Substring

SPOJ#8222 NSUBSTR"Substrings"

SPOJ#1812 LCS2"Longest Common Substrings?II"

BZOJ#3998 弦論


參考文獻(xiàn)

我們首先給出和后綴自動(dòng)機(jī)有關(guān)的一些第一手研究:

·????????A. Blumer, J. Blumer, A. Ehrenfeucht, D. Haussler, R.McConnell.Linear Size Finite Automata for the Set of All Subwordsof a Word. An Outline of Results?[1983]

·????????A. Blumer, J. Blumer, A. Ehrenfeucht, D. Haussler.The SmallestAutomaton Recognizing the Subwords of a Text[1984]

·????????Maxime Crochemore.?OptimalFactor Transducers?[1985]

·????????Maxime Crochemore.?Transducersand Repetitions?[1986]

·????????A. Nerode.?Linear automatontransformations?[1958]

此外,還有一些當(dāng)代資源,這一主題在許多有關(guān)字符串算法的書籍中被提到:

·????????Maxime Crochemore, Wowjcieh Rytter.?Jewels ofStringology?[2002]

·????????Bill Smyth.?Computing Patterns in Strings?[2003]

·????????Билл Смит.?Методы и алгоритмы вычислений на строках?[2006]


總結(jié)

以上是生活随笔為你收集整理的后缀自动机详解的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

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

日韩成人免费在线观看 | 91日韩在线视频 | 中文字幕亚洲欧美日韩 | 日日躁天天躁 | 久久成人国产精品免费软件 | 国产高清久久久久 | 五月天中文在线 | 超碰在线个人 | 97福利在线观看 | 一级片免费观看视频 | 中文字幕免费在线看 | 三级在线国产 | 国产精品1区2区3区 久久免费视频7 | 毛片永久免费 | 中文在线8新资源库 | 看黄色91 | 奇人奇案qvod | 在线99 | 中文不卡视频 | 日韩91在线| 亚洲精品国产精品国自 | 久久久久久久免费 | 天天摸天天弄 | 久久国产系列 | 91视频国产高清 | 69国产成人综合久久精品欧美 | 欧美一区二区日韩一区二区 | 久久视频免费在线 | 久久国产精品一国产精品 | 日韩有码在线播放 | av网站免费看 | 国语对白少妇爽91 | 亚洲电影av在线 | 丰满少妇在线观看网站 | 九九免费在线观看视频 | 成人三级av | 成人在线免费看 | 精品国产1区二区 | 五月激情久久久 | 玖玖在线视频观看 | 色99久久 | 亚洲一区二区三区毛片 | 欧美一区二区伦理片 | 夜夜干夜夜 | 午夜美女网站 | 午夜精品成人一区二区三区 | 人人爽人人香蕉 | 欧美日韩视频一区二区三区 | 天天干天天天 | 亚洲综合欧美精品电影 | 亚洲人成在线观看 | 99久热精品| 久久综合欧美精品亚洲一区 | 综合视频在线 | 中文字幕在线中文 | 国产精品久久久久久999 | 亚洲 综合 激情 | 日本午夜在线观看 | 超级碰碰碰视频 | 国产国语在线 | 免费午夜视频在线观看 | 超薄丝袜一二三区 | 日本电影黄色 | 97电影院网 | 亚洲综合激情网 | 国产精品一区二区av日韩在线 | 有没有在线观看av | 免费高清看电视网站 | 欧美精品网站 | 久久精品—区二区三区 | 人人艹视频 | 97超级碰| 深爱激情久久 | 国产精品久久久 | 午夜少妇一区二区三区 | 成年人视频在线免费 | 精品色999 | 91入口在线观看 | 在线成人中文字幕 | 日韩免费在线观看网站 | 久久精品3 | 久久久久久久久久久久av | 97看片网| 日韩影视在线观看 | 天堂av免费观看 | 免费高清男女打扑克视频 | 久久蜜臀一区二区三区av | 欧美一区二区精品在线 | 97色se| 免费高清av在线看 | 日韩欧美一区二区不卡 | 婷婷伊人综合 | 91传媒免费在线观看 | 日韩美女久久 | 不卡在线一区 | 伊人导航| 精品国产自在精品国产精野外直播 | 欧美日韩国产一区二 | 亚洲天堂激情 | 蜜臀av性久久久久蜜臀av | 国产成人一区二区三区在线观看 | 久久尤物电影视频在线观看 | 福利视频一区二区 | 欧美性色xo影院 | 狠狠色噜噜狠狠狠狠2021天天 | 五月开心六月伊人色婷婷 | 日韩国产高清在线 | 午夜a区 | 亚洲第一久久久 | 中文亚洲欧美日韩 | 日韩aⅴ视频 | 欧美小视频在线 | 美女精品在线 | 色天天久久 | avwww在线 | 一级免费看 | 一区二区视频免费在线观看 | 亚洲免费视频在线观看 | 97精品国产97久久久久久 | 黄色一级在线观看 | av在线播放国产 | 免费看黄色大全 | 亚洲片在线观看 | 激情五月婷婷丁香 | 视频二区在线视频 | 日本性视频 | 五月婷婷黄色 | 欧美韩日在线 | 精品成人a区在线观看 | 美女视频国产 | 成人久久久久久久久久 | 青青草国产精品 | 超碰公开在线 | 国产一区二区播放 | 狠狠色狠狠色合久久伊人 | 色噜噜噜噜 | 中中文字幕av在线 | 色综合a | 91精品久久久久久综合乱菊 | 亚州av成人 | 久草视频观看 | 911国产精品 | 91成人免费看 | 成人毛片在线观看 | 亚洲国产视频直播 | 97人人射| 国产精品专区h在线观看 | 国产成人在线免费观看 | 国产精品一区免费在线观看 | 久草在线最新 | 国产一区在线观看免费 | www178ccom视频在线 | 精品 一区 在线 | 亚洲最大激情中文字幕 | 日韩视频一区二区三区在线播放免费观看 | 99热精品久久 | 日韩乱理 | 亚洲在线视频免费 | 五月激情视频 | 亚洲婷婷综合色高清在线 | 亚洲精品麻豆视频 | 亚洲日本韩国一区二区 | 日韩一区二区三区免费视频 | 国产成a人亚洲精v品在线观看 | 夜夜骑天天操 | 91福利区一区二区三区 | 国产亚洲一级高清 | 国产精品丝袜在线 | 视频一区在线免费观看 | 怡红院成人在线 | 伊人久久av | 日韩av三区 | 亚洲精品视频第一页 | 手机在线日韩视频 | 久久精品国产精品 | 国产精品福利在线播放 | 国产精品久久久网站 | av丁香花| 免费观看第二部31集 | 丁香在线| 亚州成人av在线 | 欧美天天综合网 | 亚洲国产中文字幕在线观看 | 久草视频在线新免费 | 97精品国产91久久久久久久 | 午夜精品久久久久久久久久久久 | 天天操天天操一操 | 免费观看一级特黄欧美大片 | 波多野结衣亚洲一区二区 | 中文字幕第一页在线vr | 在线观看一区 | 香蕉久久久久久久 | 97激情影院 | 麻豆国产精品一区二区三区 | 粉嫩av一区二区三区四区 | 中文在线8新资源库 | 色偷偷88888欧美精品久久久 | 98久9在线 | 免费 | 在线精品亚洲 | 久艹视频在线免费观看 | 国产91小视频 | 国产日韩欧美精品在线观看 | 波多野结衣在线观看视频 | 欧美日韩国产高清视频 | 视频一区二区在线观看 | www.在线看片.com | 免费观看视频黄 | 999国内精品永久免费视频 | 99热精品国产 | 主播av在线 | 国产精品国内免费一区二区三区 | 婷婷国产v亚洲v欧美久久 | 国产福利小视频在线 | 中文字幕日韩免费视频 | 手机看片99| 天天av综合网 | 叶爱av在线 | 热久久免费国产视频 | 香蕉在线播放 | 日韩欧美一区二区在线 | 人人爽人人爽 | 免费观看国产精品 | 99c视频在线 | 国产精品999久久久 久产久精国产品 | www,黄视频| 欧美国产日韩一区二区三区 | 免费色视频网址 | 久久久99国产精品免费 | 波多野结衣电影一区 | 日韩欧美国产精品 | 2020天天干天天操 | 国产精品黄色av | 久久久久久久久久影院 | 999久久久久久久久 69av视频在线观看 | 国产精品一区二区三区四区在线观看 | 亚洲国产一区在线观看 | 亚洲免费av片 | 欧美日韩国产三级 | 69性欧美| 在线免费黄色 | 狠狠色狠狠色综合日日小说 | www.狠狠干| 黄色一级在线免费观看 | 国产无遮挡又黄又爽馒头漫画 | 国产视频一区二区在线播放 | 久久成人黄色 | 视频99爱 | 黄色特级片 | 97理论片| 日韩av中文字幕在线 | 色播激情五月 | 视频1区2区 | 热九九精品| 黄色一级大片在线免费看国产一 | 亚洲国产999 | 色精品视频 | 久草电影免费在线观看 | av黄色亚洲 | 天堂va欧美va亚洲va老司机 | 999成人免费视频 | 亚洲精品一区二区三区新线路 | 色中色亚洲| 五月天伊人网 | 亚洲好视频 | 久草免费福利在线观看 | 久久网址 | 日韩欧美一区二区三区在线观看 | 手机看片国产日韩 | 久草香蕉在线视频 | 在线观看免费黄视频 | 人人爱人人爽 | 色999精品| 欧美一区二区免费在线观看 | 一级a性色生活片久久毛片波多野 | 日韩成人在线免费观看 | 狠狠色丁香久久婷婷综合五月 | 成人免费视频网 | 夜夜躁狠狠燥 | 亚洲精品美女在线观看播放 | 操处女逼| 美女视频永久黄网站免费观看国产 | 99在线精品免费视频九九视 | 欧美日韩成人 | 国产91对白在线播 | 精品福利视频在线 | 久久精品国产亚洲精品2020 | 国产精品美女久久久 | 91精品国产91久久久久福利 | 国产精品久久久久久久久久直播 | 91综合视频在线观看 | 中文资源在线观看 | 免费男女羞羞的视频网站中文字幕 | 国产成免费视频 | 久久精品久久久久久久 | 国产99久久久国产 | 在线看片a | 久久国语露脸国产精品电影 | 国产在线91精品 | 免费精品在线视频 | 欧美性受极品xxxx喷水 | 国产美女视频一区 | 免费人成网 | 久草香蕉在线 | 欧美日韩国产区 | 欧美在线观看视频 | 久久国产精品成人免费浪潮 | 黄色av网站在线观看 | 96久久欧美麻豆网站 | 免费观看一区二区 | 国产拍揄自揄精品视频麻豆 | 欧美高清成人 | 国产精品对白一区二区三区 | 午夜视频免费播放 | 天天干亚洲| 中文字幕在线观 | 黄网av在线 | 久久精品xxx | 91视频啊啊啊 | 在线精品国产 | 偷拍区另类综合在线 | 国产品久精国精产拍 | 国产资源在线免费观看 | 国产在线观看,日本 | 久久香蕉电影 | 黄色亚洲精品 | 欧美va天堂va视频va在线 | 久久人91精品久久久久久不卡 | 免费看的黄色片 | 中文字幕在线人 | 黄色在线看网站 | 久久久久电影网站 | 亚洲欧美精品在线 | 国产999精品视频 | 美女视频是黄的免费观看 | 精品国产理论片 | 人人搞人人爽 | 久久久久免费观看 | 国产午夜精品一区二区三区四区 | 精品国产一二区 | 日日摸日日添日日躁av | 超碰97.com| 激情影院在线观看 | 69国产盗摄一区二区三区五区 | 久久婷婷五月综合色丁香 | 99久久精品免费看国产 | 天天操狠狠操夜夜操 | 中文字幕免费高清在线 | 在线免费日韩 | 国产精品一区二区久久 | 国产精品免费一区二区三区 | 日批视频 | 97色se| 又黄又爽又刺激 | 国产亚洲精品久久久久秋 | 免费av免费观看 | 欧美一区免费在线观看 | 99视频精品视频高清免费 | 美女网站在线看 | 久久久精品一区二区 | 国产精品mv在线观看 | 99tvdz@gmail.com| 国内精品国产三级国产aⅴ久 | 久久黄色网页 | 亚洲专区在线播放 | 黄色在线免费观看网址 | 91豆麻精品91久久久久久 | 久久久久综合精品福利啪啪 | 婷婷色资源 | 亚洲成av人片在线观看 | 欧美性生活大片 | a级片久久| 蜜臀av免费一区二区三区 | 97国产精品一区二区 | 国产精品 日韩 欧美 | 91桃色国产在线播放 | 午夜视频欧美 | 国产 日韩 欧美 在线 | 美女一级毛片视频 | av三级av | 成人黄色电影在线播放 | 欧美91精品久久久久国产性生爱 | 黄色精品久久 | 久久亚洲精品国产亚洲老地址 | 最新中文字幕在线观看视频 | 亚洲综合色视频在线观看 | 国产精品亚洲人在线观看 | 久久在线一区 | 99精品成人| 欧美亚洲精品一区 | 亚洲人成免费网站 | 欧洲精品一区二区 | 久久综合久久综合九色 | av中文字幕网址 | 91成年人网站 | 国产免费观看久久 | 手机av电影在线观看 | 日韩二区三区在线 | 韩国精品福利一区二区三区 | 国产一区二区在线视频观看 | 91精品在线免费视频 | 91在线看片 | 成年美女黄网站色大片免费看 | 国产精品第52页 | 日韩在线| 欧美日韩高清一区二区 国产亚洲免费看 | 欧美成人aa| 久久国产热 | 国产五月色婷婷六月丁香视频 | 国产不卡高清 | 日韩av免费一区二区 | 日韩中文久久 | 999久久国精品免费观看网站 | 国产精品热 | 日韩高清免费无专码区 | av三级av | 国色天香在线观看 | 日韩中文在线播放 | 久久精品亚洲 | 91禁在线看 | 四虎亚洲精品 | 久久99热精品这里久久精品 | 人人澡超碰碰97碰碰碰软件 | 黄色com| 视频一区视频二区在线观看 | 国产精品久久久久婷婷 | 亚洲精品在线二区 | 免费黄色在线播放 | 99视频精品全部免费 在线 | 国产裸体永久免费视频网站 | 91在线亚洲| 久久免费视频在线观看30 | 日韩精品播放 | 美女国产免费 | 国产最顶级的黄色片在线免费观看 | 久操免费视频 | 国产精品综合av一区二区国产馆 | 成年人在线观看视频免费 | 天天操天天操天天干 | 久久综合色影院 | 亚洲国产精品500在线观看 | 婷婷日日 | 亚洲高清国产视频 | 欧美一二三区播放 | 超碰在线成人 | 一区在线观看 | 九九九九色| 日韩一区二区三区免费视频 | 国产在线黄 | 成年人免费看片网站 | 在线黄av | 色多多污污在线观看 | 日韩精品在线一区 | 91爱爱网址 | 五月婷社区 | 精品国产一区在线观看 | 国产视频999 | 日韩免费一二三区 | 毛片黄色一级 | 最近2019好看的中文字幕免费 | 久久只精品99品免费久23小说 | 精品福利网站 | 97碰碰精品嫩模在线播放 | 成年人视频在线免费 | 久久黄色小说 | 天天射天天干天天爽 | 天天色天天射天天干 | 一级黄色在线免费观看 | 国产视频二 | av在线色| 精品久久久国产 | 成人sm另类专区 | 精品一区二区av | 午夜av一区二区三区 | 日韩在线理论 | 欧美日韩一区二区三区视频 | 97电影手机版 | 在线观看国产www | 天天操夜夜操夜夜操 | 91看片网址| 国产99久久久国产精品免费二区 | 久久99精品久久久久久三级 | 成年人网站免费在线观看 | 91亚洲欧美激情 | 久久怡红院 | 国产精品久久久久永久免费看 | 午夜精品影院 | 欧美一级片在线免费观看 | 69久久久久久久 | 91亚洲精品在线观看 | 日韩免费视频在线观看 | 99在线精品观看 | 欧美精品黑人性xxxx | 在线观看黄色的网站 | 久草网站 | 中文字幕日韩一区二区三区不卡 | 综合五月婷婷 | 日本精品视频一区 | 97夜夜澡人人双人人人喊 | 激情久久久久久久久久久久久久久久 | 日韩在线首页 | 久9在线| 黄网站污| 精品久久久久久亚洲综合网站 | 日韩毛片在线免费观看 | 91精品国产九九九久久久亚洲 | 欧美在线观看视频一区二区三区 | 亚洲午夜久久久影院 | 国产九九九视频 | 欧美日韩精品二区第二页 | sesese图片| 十八岁免进欧美 | 黄色视屏av | 激情丁香 | 久久一区二区三区超碰国产精品 | 天天操天天怕 | 91精品视频在线免费观看 | 精品国产精品国产偷麻豆 | 国产护士hd高朝护士1 | 999亚洲国产996395 | 国产一级二级三级在线观看 | 在线观看第一页 | 麻豆传媒视频观看 | 国产成人三级一区二区在线观看一 | 玖玖精品视频 | 一级欧美一级日韩 | 欧美日韩伦理在线 | 天天撸夜夜操 | 欧美日韩国产精品久久 | 婷婷激情在线 | 天天色天天爱天天射综合 | 韩国精品在线 | 婷婷丁香导航 | 少妇视频在线播放 | 久精品一区| 久久久影院官网 | 成人h动漫精品一区二 | 日韩中文在线电影 | 三级在线视频观看 | 一区二区在线影院 | 日韩专区在线 | 婷婷丁香花| 天天曰夜夜操 | 国产午夜精品福利视频 | 成人av观看 | 欧美电影黄色 | 成人h在线| 国产一级在线播放 | 97精品国产97久久久久久春色 | av日韩av | 美女在线国产 | 亚洲天天 | 久久久午夜视频 | 免费观看91 | 激情综合网色播五月 | 久久蜜桃av | 亚洲精品在 | 91网址在线看 | 国产免费不卡av | 亚洲成av人片一区二区梦乃 | av激情五月 | 国产免费av一区二区三区 | 波多野结衣在线播放视频 | 国产精彩视频一区二区 | 色婷婷狠狠五月综合天色拍 | 激情婷婷 | 欧美日韩一级在线 | 一级免费观看 | 久久99精品久久久久久久久久久久 | 久章草在线观看 | 99热超碰在线 | 91在线中文字幕 | 天天干,天天草 | 在线三级播放 | 日本久久电影网 | 日本中文字幕高清 | 日韩影视在线观看 | 精品欧美乱码久久久久久 | 国产免费影院 | 午夜在线看片 | 国产在线视频一区二区三区 | 超碰免费av | 91激情视频在线观看 | 极品久久久久 | 亚洲v欧美v国产v在线观看 | 在线观看不卡视频 | 精品毛片在线 | 欧美日韩视频一区二区三区 | 最新国产视频 | 亚洲精品动漫久久久久 | 欧美久久影院 | 欧美性极品xxxx娇小 | 狂野欧美激情性xxxx | 韩国av在线播放 | 四虎影视成人 | 久草在线免费在线观看 | 手机看片 | 99久久婷婷国产 | 中文在线字幕观看电影 | av在线直接看| 国产精品网站一区二区三区 | 欧美精品亚洲精品日韩精品 | 黄网在线免费观看 | 97色在线视频 | 久久久久电影 | 色婷婷综合久久久久中文字幕1 | 亚洲伦理一区 | 国产精彩视频一区二区 | 成人久久精品视频 | 国产精品永久免费视频 | 最新真实国产在线视频 | 欧美激情视频一区二区三区 | 一级久久精品 | 色婷婷激情电影 | 亚洲精品大全 | 91爱爱免费观看 | 欧美一区二视频在线免费观看 | 欧美一级片免费观看 | 深爱激情五月婷婷 | 久草国产在线观看 | 国产精品一区二区免费 | 欧美在线观看视频免费 | 久久精品综合 | 久久国产网 | 午夜影视剧场 | 日本中文字幕在线免费观看 | 亚洲国产精品久久久久久 | 九九热精 | 成人在线观看你懂的 | 成人av电影网址 | 黄免费在线观看 | 欧美激情精品久久久久久免费 | 91九色视频在线播放 | 欧美色一色 | 午夜精品区| 日韩精品免费一区二区 | 成人免费亚洲 | 四虎免费av | 最近最新中文字幕 | 久久视频网 | 国产精品中文字幕av | 黄色a在线观看 | 日韩精品视频网站 | 久久精品视 | 国产91精品一区二区麻豆亚洲 | 在线激情av电影 | 特级西西www44高清大胆图片 | 99精品视频在线播放免费 | 免费开视频 | 射九九| 国产精品欧美久久久久天天影视 | 亚洲综合导航 | 欧美在线你懂的 | 欧美国产三区 | 亚洲精品在线资源 | 欧美精品二 | 色综合色综合久久综合频道88 | 日韩欧美视频一区二区三区 | 欧美福利久久 | 日日摸日日爽 | 亚洲精品小视频在线观看 | 不卡的av在线 | 超碰在线98| 国产黄在线看 | 天天射天天干天天插 | 狠狠操导航 | 久久手机在线视频 | 久久精品牌麻豆国产大山 | 精品国产_亚洲人成在线 | 国产一级黄色片免费看 | 日本精品一二区 | 国产午夜精品一区二区三区欧美 | 日韩精品视频在线免费观看 | 黄色av电影在线 | 久久综合九色综合欧美就去吻 | 国产成人精品在线 | 国产理论在线 | 日本中文字幕电影在线免费观看 | 色婷婷激情综合 | 久久伊人八月婷婷综合激情 | 久久免费电影网 | 国产午夜精品一区二区三区在线观看 | 久草免费在线视频观看 | 久艹视频免费观看 | 国产亚洲欧美精品久久久久久 | 国产99中文字幕 | av在线播放网址 | 久久精品一级片 | 国产原创中文在线 | 国产成人久久精品77777 | 欧美成人黄色 | 99超碰在线播放 | 国产欧美在线一区二区三区 | 日韩伦理一区二区三区av在线 | 欧美在线观看视频一区二区三区 | 999视频网| 999久久久国产精品 高清av免费观看 | 色诱亚洲精品久久久久久 | 夜夜视频欧洲 | 在线日韩av | 久久精品久久99 | 在线免费视频一区 | 亚洲激情五月 | 国产护士av | 在线天堂日本 | 日韩欧美国产视频 | 久久草草影视免费网 | 国产97碰免费视频 | 久久精国产 | 91久久丝袜国产露脸动漫 | 亚洲在线资源 | 成人影视免费看 | 欧美日韩一区二区视频在线观看 | 国内精品久久久精品电影院 | 五月天精品视频 | 深夜精品福利 | 午夜精品电影 | 久久人人爽爽 | 超碰在线免费97 | 在线观影网站 | 免费观看av | 久久国产亚洲视频 | 国产另类xxxxhd高清 | 欧美极品少妇xxxxⅹ欧美极品少妇xxxx亚洲精品 | 黄色在线观看免费 | 特黄特色特刺激视频免费播放 | 久操97| 精品视频久久 | 成人小视频免费在线观看 | 国产三级精品三级在线观看 | 黄色电影网站在线观看 | 91麻豆网 | 欧美韩国日本在线 | 日韩影片在线观看 | 丰满少妇在线观看 | 欧美激情精品久久久久久免费印度 | 91精品一区二区三区久久久久久 | 视频二区 | 婷婷激情综合五月天 | av大全在线看| 久久免费看毛片 | 99中文在线 | 日本一区二区免费在线观看 | 日本中文乱码卡一卡二新区 | 日日夜夜精品视频天天综合网 | 日韩视频在线观看免费 | 国色天香在线观看 | 成人黄色在线视频 | 天天爱av导航 | 久久调教视频 | 国产一区二区三区免费视频 | 欧美午夜寂寞影院 | 亚洲精品人人 | 免费看av片网站 | 在线观看亚洲精品 | 国产91在线看 | 精品99免费 | 免费黄色小网站 | 美女视频是黄的免费观看 | 亚洲国产精品激情在线观看 | 色偷偷88888欧美精品久久久 | 国产免费又爽又刺激在线观看 | 免费的黄色的网站 | 亚洲精品视 | 99精品久久久久久久久久综合 | 成人影音在线 | 99久久国产免费看 | 精品国产精品一区二区夜夜嗨 | 亚洲一区免费在线 | 狠狠狠狠狠狠狠狠 | 91视频久久久 | 久热免费| 久久国产免费 | 日韩欧美精品在线观看视频 | 亚洲一区二区三区91 | 91在线精品秘密一区二区 | 午夜精品久久久久久久99 | 成人午夜av电影 | 丁香伊人网 | 97精品国产手机 | 9草在线| 久久精品女人毛片国产 | 九九热精品在线 | 狠狠干我 | 国产精品乱码久久久久久1区2区 | 免费看的黄色片 | 黄色av网站在线免费观看 | 日韩精品视频在线免费观看 | 亚洲欧美日韩精品久久奇米一区 | 超碰人在线 | 亚洲激情免费 | 肉色欧美久久久久久久免费看 | 91精品国产成人www | 一级α片免费看 | 免费高清无人区完整版 | 亚洲第一色 | 国产精品九九久久99视频 | 亚洲涩涩涩 | 日韩欧美精品在线 | 成人av免费在线 | 国产美女无遮挡永久免费 | 久久精品99精品国产香蕉 | 麻豆精品国产传媒 | 一区二区三区免费在线观看视频 | 五月婷婷综合在线观看 | 国产精品视频免费观看 | 97在线看| 99精品欧美一区二区 | 丁香六月国产 | 欧美一二三区在线播放 | 在线观看日韩免费视频 | 国产精品18久久久久久久久久久久 | 三级黄色欧美 | 永久黄网站色视频免费观看w | 亚洲精品国偷拍自产在线观看 | 999热视频 | 欧美日韩视频免费看 | 亚洲日本va在线观看 | 免费视频91 | 成年人网站免费在线观看 | 麻豆久久| 久久久高清视频 | 手机看片午夜 | 久久综合狠狠综合久久综合88 | 99在线热播| 激情五月视频 | 久久香蕉国产精品麻豆粉嫩av | 麻豆精品视频在线观看免费 | 国产在线观看午夜 | 亚洲精品网页 | 美女免费av| 欧美日韩国产一二 | 色婷婷播放 | 四虎国产视频 | 国产小视频免费在线网址 | 五月婷网站 | 国产一级在线看 | 日韩精品一区二区三区水蜜桃 | 在线观看国产一区二区 | 亚洲精品www | 久章草在线 | 999久久久久久久久久久 | 亚洲视频aaa | 国产精品国产三级国产 | 99在线视频免费观看 | 久久综合给合久久狠狠色 | 伊人官网 | 久久久久亚洲精品中文字幕 | 人人看黄色 | 最新中文字幕视频 | 一区二区视频欧美 | 黄色录像av | 黄色毛片观看 | 欧美少妇影院 | 婷婷伊人综合亚洲综合网 | 日产乱码一二三区别免费 | 亚洲va综合va国产va中文 | 日韩欧美视频免费在线观看 | 欧美巨大 | 欧美性做爰猛烈叫床潮 | 91黄色在线看 | 午夜精品久久久久久久99水蜜桃 | 欧美性成人 | 日韩免费一区二区三区 | 欧美色精品天天在线观看视频 | 在线观看中文字幕dvd播放 | 三级在线国产 | 亚洲成年人在线播放 | 91九色精品女同系列 | 人人澡人人爱 | 欧美va天堂在线电影 | 美女禁18| 欧美精品成人在线 | 久久久久久久久久久久99 | 三级黄免费看 | 中文字幕网站 | 亚洲最大的av网站 | 久久久婷 | 国产在线精品观看 | 国产精品初高中精品久久 | 狠狠躁夜夜躁人人爽超碰91 | 在线观看视频一区二区三区 | 狠狠网亚洲精品 | 在线观看欧美成人 | 一区二区三区视频 | 天天操天天色天天射 | 91福利免费| 国产精品入口久久 | 国产精品久久久久久久久久久杏吧 | 日韩在线观看a | 亚洲黄色在线播放 | 国产成人av免费在线观看 | 一区二区三区在线播放 | 国产精品久久久久久欧美 | 中文字幕欧美激情 | 国产麻豆剧传媒免费观看 | 日三级在线 | 狠狠躁天天躁 | 久久99久久精品国产 | av在线免费不卡 | av色图天堂网| 久久国产精品99久久久久久老狼 | 中文字幕在线视频一区 | 日日摸日日碰 | av韩国在线| 久久不卡日韩美女 | 久久精品8 | 99草视频在线观看 | 精品在线一区二区三区 | 欧美精品久久久久久久久免 | 日韩av电影中文字幕在线观看 | 国产精品女同一区二区三区久久夜 | 香蕉久久久久久av成人 | 激情av综合| 欧美a级在线免费观看 | 98涩涩国产露脸精品国产网 | 免费视频在线观看网站 | 欧美日韩国产综合一区二区 | 免费网站黄色 | 国产日本在线观看 | 91黄色免费看 | 综合久久网站 | 中文字幕av最新更新 | 天堂网在线视频 | 精品国产一区二区三区久久久 | 日韩视频一区二区三区 | 久久久久久久久久毛片 | 九九热在线精品 | 久久天堂亚洲 | 99精品小视频 | 国产精品久久久免费看 | 国产又粗又猛又色又黄网站 | 一区二区三区精品在线视频 | 中文字幕黄色网址 | 国产精品成人自产拍在线观看 | 亚洲视频免费视频 | 欧美色图一区 | 人人看人人艹 | 欧美人体xx| 国产精品毛片久久蜜 | 在线 成人| 黄色在线网站噜噜噜 | 日本不卡一区二区 | 99视频精品视频高清免费 | 欧美久久综合 | 日日操狠狠干 | 久久久综合| 国产精品一区二区三区在线看 | 五月婷婷六月丁香在线观看 | 麻豆视屏 | 亚洲欧美日韩精品久久久 | 最近日本韩国中文字幕 | 五月天激情视频在线观看 | 黄色一级大片在线免费看产 | 国产一级视频免费看 | 四虎国产精品免费观看视频优播 | 久久国产精品成人免费浪潮 | 91污在线观看 | 99久久er热在这里只有精品15 | 欧美性大战| 亚洲做受高潮欧美裸体 | 日韩三级免费观看 | 日日操夜| 中文区中文字幕免费看 | 天天操天天射天天添 | 久久香蕉国产精品麻豆粉嫩av | 亚洲国产日韩一区 | 欧美视频日韩视频 | 久久成人高清 | 欧美日韩国产一区二区三区在线观看 | 97精品久久人人爽人人爽 | 亚洲欧美视频在线 | 欧美一级性生活视频 | 国产成人久 | 伊人久久国产精品 | 激情欧美一区二区三区免费看 | 国产一级电影免费观看 | 91资源在线| 91在线观| 永久黄网站色视频免费观看w | 夜夜操夜夜干 | 午夜黄色一级片 | 99精品观看 | 日韩免费电影一区二区三区 | 日本久热 | 国产精品9999久久久久仙踪林 | 成人h视频在线播放 | 91完整视频| 欧美最新另类人妖 | 色99中文字幕 | 久久免费一 | 欧美做受高潮电影o | 色多多视频在线观看 | 天天色天天操天天爽 | 黄色软件视频大全免费下载 | 亚洲高清视频在线观看免费 | 操天天操 | 久久婷婷色综合 | 久久九九影视 |