• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            后綴數(shù)組

            Posted on 2011-10-23 16:51 Mato_No1 閱讀(2851) 評(píng)論(2)  編輯 收藏 引用 所屬分類: 字符串匹配
            【后綴數(shù)組真難懂啊啊……就20+行的代碼搞了幾天才理解……不知是不是我太沙茶了】

            【1】一些定義:
            字符串:廣義的字符串是指“元素類型有序,且元素值有一定范圍的序列”,其元素不一定非要是字符,可以是數(shù)字等,因此整數(shù)、二進(jìn)制數(shù)等也是字符串;
            字符集:字符串的元素值的范圍稱為字符集,其大小記為SZ。
            字符串的長(zhǎng)度:字符串中元素的個(gè)數(shù),一般記為N,長(zhǎng)度為N的字符串A第一次提到時(shí)一般用A[0..N-1]來(lái)表示;
            前綴:字符串A[0..N-1]的從A[0]開(kāi)始的若干個(gè)連續(xù)的字符組成的字符串稱為A的前綴,以下“前綴i”或者“編號(hào)為i的前綴”指的都是A[0..i];
            后綴:字符串A[0..N-1]的到A[N-1]終止的若干個(gè)連續(xù)的字符組成的字符串稱為A的后綴,以下“后綴i”或者“編號(hào)為i的后綴”指的都是A[i..N-1];

            對(duì)于一個(gè)長(zhǎng)度為N的字符串,將其N個(gè)后綴按字典序大小進(jìn)行排序,得到兩個(gè)數(shù)組sa[i]和rank[i],sa[i]為排在第i位的后綴的編號(hào)(也就是一般說(shuō)的ord[i]),rank[i]為排在后綴i排在的位置(稱為后綴i的名次)。sa、rank值的范圍均為[0..N-1]。sa和rank互逆,即sa[i]=j等價(jià)于rank[j]=i,或者說(shuō)成sa[rank[i]]=rank[sa[i]]=i。這里,sa稱為后綴數(shù)組,rank稱為名次數(shù)組

            【2】用倍增算法求后綴數(shù)組:
            在論文里,后綴數(shù)組有兩種求法:倍增算法和DC3算法,前者的時(shí)間復(fù)雜度為O(NlogN),但常數(shù)較小,后者的時(shí)間復(fù)雜度為O(N),但常數(shù)較大,在實(shí)際應(yīng)用中,兩者的總時(shí)間相差不大,且后者比前者難理解得多(本沙茶理解前者都用了幾天時(shí)間……后者就木敢看了)。這里就總結(jié)一下倍增算法吧囧……
            首先,貼一下本沙茶的用倍增算法求后綴數(shù)組的模板:
            void suffix_array()
            {
                
            int p, v0, v1, v00, v01;
                re(i, SZ) S[i] 
            = 0;
                re(i, n) rank[i] 
            = A[i];
                re(i, n) S[A[i]]
            ++;
                re2(i, 
            1, SZ) S[i] += S[i - 1];
                rre(i, n) sa[
            --S[A[i]]] = i;
                
            for (int j=1; j<n; j<<=1) {
                    p 
            = 0; re2(i, n-j, n) tmp[p++= i;
                    re(i, n) 
            if (sa[i] >= j) tmp[p++= sa[i] - j;
                    re(i, SZ) S[i] 
            = 0;
                    re(i, n) S[rank[i]]
            ++;
                    re2(i, 
            1, SZ) S[i] += S[i - 1];
                    rre(i, n) sa[
            --S[rank[tmp[i]]]] = tmp[i];
                    tmp[sa[
            0]] = p = 0;
                    re2(i, 
            1, n) {
                        v0 
            = sa[i - 1]; v1 = sa[i];
                        
            if (v0 + j < n) v00 = rank[v0 + j]; else v00 = -1;
                        
            if (v1 + j < n) v01 = rank[v1 + j]; else v01 = -1;
                        
            if (rank[v0] == rank[v1] && v00 == v01) tmp[sa[i]] = p; else tmp[sa[i]] = ++p;
                    }
                    re(i, n) rank[i] 
            = tmp[i];
                    SZ 
            = ++p;
                }
            }
            這里A是待求sa和rank的字符串。

            <1>倍增算法的思想:
            記R[i][j]為A[i..i+2j-1](如果越界,則后面用@填充)在A的所有長(zhǎng)度為2j的子串(越界則后面用@填充)中的名次(rank)值。倍增算法就是按階段求出所有R[i][j]的值,直到2j>N為止。首先,R[i][0]的就是字符A[i]在A[0..N-1]中的名次,是可以直接用計(jì)數(shù)排序來(lái)實(shí)現(xiàn)的。然后,若R[0..N-1][j-1]已知,則可以按照以下方法求出R[0..N-1][j]的值:對(duì)每個(gè)i(0<=i<N),構(gòu)造一個(gè)二元組<Xi, Yi>,其中Xi=R[i][j-1],Yi=R[i+2j][j-1](若i+2j>=N,則Yi=-∞),然后對(duì)這N個(gè)二元組按照第一關(guān)鍵字為X,第二關(guān)鍵字為Y(若兩者都相等則判定為相等)進(jìn)行排序(可以用基數(shù)排序來(lái)實(shí)現(xiàn)),排序后,<Xi, Yi>的名次就是的R[i][j]的值。

            <2>一開(kāi)始,對(duì)A中的各個(gè)字符進(jìn)行計(jì)數(shù)排序:
            re(i, SZ) S[i] = 0;
            re(i, n) rank[i] 
            = A[i];
            re(i, n) S[A[i]]
            ++;
            re2(i, 
            1, SZ) S[i] += S[i - 1];
            rre(i, n) sa[
            --S[A[i]]] = i;
            這個(gè)木有神馬好說(shuō)的,在搞懂了基數(shù)排序之后可以秒掉。唯一不同的是這里加了一句:rank[i]=A[i],這里的rank[i]是初始的i的名次,MS不符合rank[i]的定義和sa與rank間的互逆性。這里就要解釋一下了囧。因?yàn)樵谇髎a的過(guò)程中,rank值可能不符合定義,因?yàn)殚L(zhǎng)度為2j的子串可能會(huì)有相等的,此時(shí)它們的rank值也要相等,而sa值由于有下標(biāo)的限制所以不可能有相等的。因此,在過(guò)程中,rank其實(shí)是用來(lái)代替A的子串的,這樣rank值只需要表示一個(gè)“相對(duì)順序”就行了,也就是:rank[i0]>(=, <)rank[i1],當(dāng)且僅當(dāng)A[i0..i0+2j-1]>(=, <)A[i1..i1+2j-1]。這樣,可以直接將A[i]值作為初始的rank[i]值。

            <3>j(代替2j)的值從1開(kāi)始不斷倍增,對(duì)二元組進(jìn)行基數(shù)排序求出新階段的sa值:
            for (int j=1; j<n; j<<=1) {
                p 
            = 0; re2(i, n-j, n) tmp[p++= i;
                re(i, n) 
            if (sa[i] >= j) tmp[p++= sa[i] - j;
                re(i, SZ) S[i] 
            = 0;
                re(i, n) S[rank[i]]
            ++;
                re2(i, 
            1, SZ) S[i] += S[i - 1];
                rre(i, n) sa[
            --S[rank[tmp[i]]]] = tmp[i];
            注意這個(gè)基數(shù)排序的過(guò)程是很特別的。首先,它并不是對(duì)A在進(jìn)行排序,而是對(duì)上一階段求出的rank在進(jìn)行排序。因?yàn)榍懊嬉呀?jīng)說(shuō)過(guò),在求sa的過(guò)程中,rank就是用來(lái)代替A的對(duì)應(yīng)長(zhǎng)度的子串的,由于不能直接對(duì)子串進(jìn)行排序(那樣的話時(shí)間開(kāi)銷很恐怖的),所以只能對(duì)rank進(jìn)行排序。另外,這里在對(duì)二元組<x, y>的第二關(guān)鍵字(y)進(jìn)行排序的過(guò)程中加了優(yōu)化:這些y其實(shí)就是把上一階段的sa整體左移了j,右邊空出的部分全部用@(空串)填充得到的,由于空串的字典序肯定最小,因此將右邊的空串按照下標(biāo)順序先寫(xiě)入臨時(shí)sa(代碼中用tmp表示的就是臨時(shí)sa,也就是對(duì)第二關(guān)鍵字y排序后的ord結(jié)果),然后,上一階段的sa如果左移后還木有消失的(也就是sa值大于等于j的),再按順序?qū)懭肱R時(shí)sa,就得到了排序結(jié)果。剩下的對(duì)x的排序結(jié)果就是上一階段的sa,唯一不同的是對(duì)于x相同的,按照臨時(shí)名次遞增的順序。

            <4>求出新階段的rank值:
            tmp[sa[0]] = p = 0;
            re2(i, 
            1, n) {
                v0 
            = sa[i - 1]; v1 = sa[i];
                
            if (v0 + j < n) v00 = rank[v0 + j]; else v00 = -1;
                
            if (v1 + j < n) v01 = rank[v1 + j]; else v01 = -1;
                
            if (rank[v0] == rank[v1] && v00 == v01) tmp[sa[i]] = p; else tmp[sa[i]] = ++p;
            }
            re(i, n) rank[i] 
            = tmp[i];
            SZ 
            = ++p;
            由于下一階段需要使用本階段的rank值,因此在求出了本階段的sa值以后,需要求rank值。(代碼中的tmp起了臨時(shí)rank的作用,目的是節(jié)省空間)
            因?yàn)閟a值已經(jīng)求出,因此只要依次掃描sa就可以得到rank值,唯一要做的工作就是找到哪些子串是相等的,它們的rank值應(yīng)該相等,除此之外,rank值只要依次加1即可。判定相等的方法:只需判定rank[i]和rank[i+j]是否都對(duì)應(yīng)相等即可。若rank[i+j]越界,用-∞(當(dāng)然任何一個(gè)負(fù)數(shù)都行,代碼中用了-1)來(lái)表示。
            最后還有一個(gè)優(yōu)化:由于本階段的名次的范圍只有[0..p]這么多,下一階段的“字符集”(其實(shí)就是rank集)的大小SZ可以設(shè)為p+1,這樣可以省一些時(shí)間。

            這樣后綴數(shù)組sa和名次數(shù)組rank就全部求完了。

            以后還有一些更重要的東東就是AC自動(dòng)機(jī)、后綴數(shù)組等的應(yīng)用問(wèn)題,算了,以后再搞吧囧。

            Feedback

            # re: 后綴數(shù)組[未登錄](méi)  回復(fù)  更多評(píng)論   

            2012-05-25 21:42 by
            撒旦

            # re: 后綴數(shù)組  回復(fù)  更多評(píng)論   

            2012-06-02 19:11 by autisyu
            左移還是右移?
            久久亚洲精品成人无码网站| 1000部精品久久久久久久久| 久久精品国产清自在天天线| 久久免费大片| 久久久无码一区二区三区| 青青国产成人久久91网| 看全色黄大色大片免费久久久| 久久久久国产精品嫩草影院 | 热re99久久精品国99热| 久久婷婷五月综合97色| 久久国产影院| 91精品国产9l久久久久| 亚洲乱码日产精品a级毛片久久| 欧美噜噜久久久XXX| 久久这里都是精品| 国产成人久久777777| 亚洲欧美伊人久久综合一区二区 | 亚洲伊人久久综合中文成人网| 亚洲国产另类久久久精品黑人| 国产亚洲美女精品久久久| 97精品依人久久久大香线蕉97 | 曰曰摸天天摸人人看久久久| 久久精品国产AV一区二区三区 | 99精品国产在热久久| 97精品伊人久久大香线蕉| 久久久噜噜噜久久中文字幕色伊伊| 熟妇人妻久久中文字幕| 国内精品久久久久影院老司 | 青青青国产精品国产精品久久久久| 99久久夜色精品国产网站| 三级韩国一区久久二区综合 | 亚洲色婷婷综合久久| 久久综合久久综合亚洲| 久久99精品国产麻豆不卡| 亚洲天堂久久精品| 久久精品免费观看| 久久噜噜电影你懂的| 91久久精品电影| 久久99久久无码毛片一区二区| 国产精品99久久精品爆乳| 狠狠色伊人久久精品综合网|