POJ 3261 Milk Patterns 后綴數(shù)組

Openjudge原題鏈接

  • 題意
    輸入一個(gè)長(zhǎng)度N(<=20000)的數(shù)組窘疮,求出其重復(fù)K次的最長(zhǎng)可重疊子串
  • 思路
    由于N可以達(dá)到20000,故考慮O(NlogN)的算法,于是想到后綴數(shù)組。
    假設(shè)取出了數(shù)組的全部后綴端姚,那么重復(fù)K次的最長(zhǎng)可重疊子串就是在全部后綴中出現(xiàn)K次的最長(zhǎng)前綴。所以需要將后綴用倍增方法排序挤悉,并計(jì)算出相鄰串的公共前綴長(zhǎng)度(h數(shù)組)渐裸,用RMQ分別查找[1...K],[2...K+1]...[N-K...N-1]的區(qū)間最小值,最大的一個(gè)就是重復(fù)了K次的可重疊子串長(zhǎng)度装悲。
  • 難點(diǎn)在于求后綴數(shù)組和使用RMQ昏鹃。
  • 后綴數(shù)組的求法:sa[i]表示排第i的字符串是哪個(gè),用rk[i] 第i個(gè)字符串排第幾(相同的字符串有一樣的排名)衅斩。根據(jù)所有后綴的前n個(gè)字符計(jì)算出rk數(shù)組盆顾,根據(jù)rk數(shù)組更新sa數(shù)組。然后根據(jù)后綴的前2n個(gè)字符計(jì)算出新的rk數(shù)組.
    為什么可以這樣做呢畏梆?因?yàn)?strong>suffix(i,2n)=suffix(i,n)+suffix(i+n,n)您宪。
    故前2n個(gè)字符的排名可以看成2元組(rk[i],rk[i+n])。用這個(gè)二元組進(jìn)行排序即可奠涌。
    這樣宪巨,每次計(jì)算rk和sa是O(n)的時(shí)間,而一共進(jìn)行了O(logn)趟溜畅,故計(jì)算rk和sa耗時(shí)O(nlogn)捏卓。
  • 然后,求h數(shù)組 h[k]=LCP(suffix[sa[k]],suffix[sa[k+1]])慈格,這時(shí)候再一次利用后綴字符串的性質(zhì)怠晴。
    假設(shè)我們已經(jīng)求得h[k]=LCP(suffix[sa[k]],suffix[sa[k+1]]),假設(shè)sa[k]=i,sa[k+1]=j遥金。
    那么LCP(suffix(i+1),suffix(j+1))=max(h[k]-1,0).找到i+1在sa中的位置t(t=rk(i+1)),sa[t+1]=p.
    即h[t]=LCP(suffix(i+1),suffix(p))
    而suffix(i)<suffix(j) => suffix(i+1)<suffix(j+1)
    而suffix(p)緊鄰suffix(i+1),故有
    suffix(i+1)<suffix(p)<=suffix(j+1)
    所以有h[t]>=max(h[k]-1,0),繼續(xù)匹配則得到h[t]
  • RMQ是一種用O(nlogn)時(shí)間預(yù)處理蒜田,O(1)時(shí)間求區(qū)間最小值的數(shù)據(jù)結(jié)構(gòu)稿械,用dp實(shí)現(xiàn),其空間復(fù)雜度為O(nlogn)冲粤。
    用dp[i][j]表示[i,i+2^j-1]的區(qū)間最小值美莫,那么轉(zhuǎn)移方程就是
    dp[i][j]=min(dp[i][j-1],dp[i+(1<<(j-1))][j-1])
    那么為了查詢區(qū)間[l,r]的最小值,只需要找到最大的k,s.t. (1<<k)<=(r-l+1)梯捕,則
    ans=min(dp[l][k],dp[r-(1<<k)+1][k].要注意這里的邊界條件.
  • AC代碼
/*
    輸入一個(gè)長(zhǎng)度N(<=20000)的數(shù)組厢呵,求出其重復(fù)K次的最長(zhǎng)可重疊子串
    由于N可以達(dá)到20000,故考慮O(NlogN)的算法傀顾,于是想到后綴數(shù)組襟铭。
    假設(shè)取出了數(shù)組的全部后綴,那么重復(fù)K次的最長(zhǎng)可重疊子串就是在全
    部后綴中出現(xiàn)K次的最長(zhǎng)前綴锣笨。所以需要將后綴用倍增方法排序蝌矛,并計(jì)
    算出相鄰串的公共前綴長(zhǎng)度(h數(shù)組),用RMQ分別查找[1...K],
    [2...K+1]...[N-K...N-1]的區(qū)間最小值错英,最大的一個(gè)就是重復(fù)了K
    次的可重疊子串長(zhǎng)度。
*/
#include<iostream>
#include<algorithm>
#include<cstring>
#include<string>
using namespace std;
const int N = 1e5+5;
const int LOGN = 20;
const int M = 1e6 + 5;
int a[N], sa[N], rk[N], h[N];// sa[i] 排第i的字符串是哪個(gè); rk[i] 第i個(gè)字符串排第幾
int cnt[M];
int t1[N], t2[N];//tmp array
int dp[N][LOGN];
int n, K;
void build_sa(int* s) {
    memset(cnt, 0, sizeof(cnt));
    int m = 0;
    for (int i = 1; i <= n; i++) {
        m = max(m, a[i]);//m for max
        cnt[s[i]]++;
        t1[i] = s[i];
    }
    for (int i = 1; i <= m; i++) {
        cnt[i] += cnt[i - 1];
    }
    int t = 1;
    for (int i = 1; i <= n; i++) {
        rk[i] = cnt[t1[i]];
    }
    for (int i = 1; i <= n; i++) {
        sa[cnt[t1[i]]--] = i;//初始化sa
    }
    /*a[0] = -1;
    for (int i = 1; i <= n; i++) {//初始化rk
        if (a[sa[i]] != a[sa[i - 1]])
            rk[sa[i]] = rk[sa[i - 1]] + 1;
        else rk[sa[i]] = rk[sa[i - 1]];
    }*/

    for (int k = 1; k <= n; k <<= 1)
    {
        int p = 0;
        for (int i = n - k + 1; i <= n; i++) t2[++p] = i;
        for (int i = 1; i <= n; i++) if (sa[i] > k) t2[++p] = sa[i] - k;

        for (int i = 0; i <= m; i++) cnt[i] = 0;
        for (int i = 1; i <= n; i++) cnt[t1[i]]++;
        for (int i = 1; i <= m; i++) cnt[i] += cnt[i - 1];
        /* 雙關(guān)鍵字排序 */
        for (int i = n; i >= 1; i--) sa[cnt[t1[t2[i]]]--] = t2[i];//wtf,根據(jù)rk(t1)更新sa,但這到底是在做什么B”椭岩!

        for (int i = 1; i <= n; i++) swap(t1[i], t2[i]);//t2記錄原先的t1
        p = 0; t1[sa[1]] = ++p;
        /* 計(jì)算t1為新的rk,即以前2^k個(gè)字符排序得到的rk */
        for (int i = 2; i <= n; i++)
            t1[sa[i]] = (t2[sa[i]] == t2[sa[i - 1]] &&
                t2[sa[i] + k] == t2[sa[i - 1] + k]) ? p : ++p;
        m = p; if (m >= n) break;//優(yōu)化策略
    }

    for (int i = 1; i <= n; i++) rk[sa[i]] = i;
    /* 根據(jù) rk,sa 計(jì)算h數(shù)組 */
    /* h[k]=LCP(suffix[sa[k-1]],suffix[sa[k]]) */
    int p = 0;
    for (int i = 1; i <= n; i++)
    {
        if (rk[i] == 1) p = 0;
        else
        {
            if (p) p--;
            while (i + p <= n && sa[rk[i] - 1] + p <= n
                && s[i + p] == s[sa[rk[i] - 1] + p]) p++;
        }
        h[rk[i]] = p;
    }
    /*求h數(shù)組 h[k]=LCP(suffix[sa[k]],suffix[sa[k+1]]) */
    /*int o, b;
    for (int i = 0; i < n; i++) {
        o = rk[i];//在sa數(shù)組中的位置
        if (o == n) continue;
        b = sa[o + 1];//b為要和i匹配的后綴的位置
        while (i+h[o]<=n&&b+h[o]<=n
            &&s[i + h[o]] == s[b + h[o]]) ++h[o];
        h[rk[i + 1]] = max(0, h[o] - 1);
    }*/
    /*
    for (int i = 1; i <= n; i++) {
        cout << rk[i] << " ";
    }
    cout << endl;
    for (int i = 1; i <= n; i++) {
        cout << h[i] << " ";
    }
    cout << endl;
    for (int i = 1; i <= n; i++) {
        for (int j = sa[i]; j <= n; j++) {
            cout << a[j];
        }
        cout << endl;
    }
    cout << endl;*/
}
/* dp[i][j]表示[i,i+2^j-1]的區(qū)間最小值              */
/* dp[i][j]=min(dp[i][j-1],dp[i+(1<<(j-1))][j-1])   */
void rmq_init() {
    int len = n;
    for (int i = 1; i <= len; i++)
        dp[i][0] = h[i];
    for (int j = 1; (1 << j) <= len; j++) {
        for (int i = 1; i + (1 << j) - 1 <= len; i++) {
            dp[i][j] = min(dp[i][j - 1], dp[i + (1 << (j - 1))][j - 1]);
        }
    }
}
/* 查詢h[l...r]上的區(qū)間最小值    */      
int rmq(int l, int r) {
    int k = 0;
    while ((1 << (k + 1)) <= r - l + 1)
        k++;
    int ans = min(dp[l][k], dp[r - (1 << k) + 1][k]);
    return ans;
}
void solve() {
    rmq_init();
    int ans = -1;
    for (int i = 1; i <= n - K+1; i++) {
        ans = max(ans, rmq(i+1, i + K - 1));//h[i+1]...h[i+K-1]
    }
    cout << ans << endl;
}
int main() {
    cin >> n >> K;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
    }
    build_sa(a);
    solve();
    return 0;
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末璃赡,一起剝皮案震驚了整個(gè)濱河市判哥,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌碉考,老刑警劉巖塌计,帶你破解...
    沈念sama閱讀 222,627評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異侯谁,居然都是意外死亡锌仅,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,180評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門墙贱,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)热芹,“玉大人,你說(shuō)我怎么就攤上這事惨撇∫僚В” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 169,346評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵魁衙,是天一觀的道長(zhǎng)报腔。 經(jīng)常有香客問(wèn)我株搔,道長(zhǎng),這世上最難降的妖魔是什么纯蛾? 我笑而不...
    開(kāi)封第一講書人閱讀 60,097評(píng)論 1 300
  • 正文 為了忘掉前任邪狞,我火速辦了婚禮,結(jié)果婚禮上茅撞,老公的妹妹穿的比我還像新娘帆卓。我一直安慰自己,他們只是感情好米丘,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,100評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布剑令。 她就那樣靜靜地躺著,像睡著了一般拄查。 火紅的嫁衣襯著肌膚如雪吁津。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書人閱讀 52,696評(píng)論 1 312
  • 那天堕扶,我揣著相機(jī)與錄音碍脏,去河邊找鬼。 笑死稍算,一個(gè)胖子當(dāng)著我的面吹牛典尾,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播糊探,決...
    沈念sama閱讀 41,165評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼钾埂,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了科平?” 一聲冷哼從身側(cè)響起褥紫,我...
    開(kāi)封第一講書人閱讀 40,108評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎瞪慧,沒(méi)想到半個(gè)月后髓考,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,646評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡弃酌,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,709評(píng)論 3 342
  • 正文 我和宋清朗相戀三年氨菇,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片矢腻。...
    茶點(diǎn)故事閱讀 40,861評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡门驾,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出多柑,到底是詐尸還是另有隱情奶是,我是刑警寧澤,帶...
    沈念sama閱讀 36,527評(píng)論 5 351
  • 正文 年R本政府宣布,位于F島的核電站聂沙,受9級(jí)特大地震影響秆麸,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜及汉,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,196評(píng)論 3 336
  • 文/蒙蒙 一沮趣、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧坷随,春花似錦房铭、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 32,698評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至类溢,卻和暖如春凌蔬,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背闯冷。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,804評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工砂心, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人蛇耀。 一個(gè)月前我還...
    沈念sama閱讀 49,287評(píng)論 3 379
  • 正文 我出身青樓辩诞,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親蒂窒。 傳聞我的和親對(duì)象是個(gè)殘疾皇子躁倒,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,860評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容

  • 你剛剛上完課,就開(kāi)始了白日夢(mèng)洒琢,沉迷在與他幻想的相遇中。 “想誰(shuí)呢褐桌?” “明知故問(wèn)衰抑。” 是荧嵌,我是明知故問(wèn)呛踊,但我在你幻...
    暗戀君閱讀 153評(píng)論 0 1
  • 騰訊Bugly,為移動(dòng)開(kāi)發(fā)者提供專業(yè)的異常上報(bào)啦撮,運(yùn)營(yíng)統(tǒng)計(jì)和內(nèi)測(cè)分發(fā)解決方案谭网,幫助開(kāi)發(fā)者快速發(fā)現(xiàn)并解決異常,同時(shí)掌握...
    0o凍僵的企鵝o0閱讀 2,060評(píng)論 1 8
  • 現(xiàn)在很多父母都很注重親子閱讀,會(huì)陪孩子讀各式各樣的繪本,也有很多推薦繪本的書單供家長(zhǎng)們參考锥涕。那么衷戈,又有多少家長(zhǎng)會(huì)給...
    李鳳玲_默契閱讀 664評(píng)論 2 7