後綴數組學習與應用

學習步驟

首先明確幾個概念:

sa[i]:即,排名爲i的後綴的起點下標
rank[i]:即,第i個後綴的排名
height[i]:即,sa[i]和sa[i-1]的最長公共前綴
h[i]:即,height[rank[i]],即第i個後綴與前一名的最長公共前綴

一些結論

結論一:
定義:LCP(i,j)=lcp(suffix(sa[i],suffix(sa[j])
則轉化:LCP(i,j)=min(height[k]) ,sa[i]+1≤k≤sa[j]
結論二:
suffix[i]的與其他後綴的最長公共前綴爲max(height[sa[i]],height[sa[i]+1])
結論三:
h[i]h[i1]1 ,理由suffix[i-1]去掉一個字符後的suffix[i],必然存在另一個後綴也去掉相同字符後的的匹配長度爲h[i-1]-1;由結論二可知,h[i]h[i1]1 ;

瞭解一下後綴排序過程

這裏寫圖片描述

其中x爲上一次排序的rank,而y爲第二關鍵字的rank,兩者組合後的排序爲本次的rank

貼一份好博客

poj2774
題意:問兩個字符串的最長公共子串
題解:兩個字符串拼接,中間用特殊字符隔開,然後後綴數組模版,詳見代碼

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=200005;
int a[maxn];//
int sa[maxn],height[maxn],rank[maxn],tax[maxn],y[maxn];
//sa[i]排名爲i的後綴的起始下標;rank[i]起始下標爲i的後綴的排名;
//height[i]排名爲i的後綴和前一名的lcp;tax[i]輔助計數排序,記錄的是排名爲i的個數,它維護一個前綴
//y[i]維護的是第二關鍵字 ,表示排名爲i的是那個第二關鍵字與第y[i]個第一關鍵字組合排序
int n,m;//n爲字符串長度,m爲ascii碼個數,也是排名種數。

void rsort() {
    //rank爲第一關鍵字,y爲第二關鍵字

    for(int i=0; i<=m; i++)tax[i]=0;
    for(int i=1; i<=n; i++)tax[rank[y[i]]]++; //用第一關鍵字對第二關鍵字的排名進行離散化
    for(int i=1; i<=m; i++)tax[i]+=tax[i-1]; //tax[i]表示的是排名
    for(int i=n; i>=1; i--)sa[tax[rank[y[i]]]--]=y[i];
}
int cmp(int *f, int x, int y, int w) {
    return f[x] == f[y] && f[x + w] == f[y + w];
}

void suffix() {
    for(int i=1; i<=n; i++)rank[i]=a[i],y[i]=i;
    m=128;
    rsort();//rsort後得到本輪排序的sa

    for(int w=1,p=1,i; p<n; w+=w,m=p) { //p表示本輪排序的rank總類數,當有n種rank時,即p=n,排序結束
        for (p = 0, i = n - w + 1; i <= n; i ++) y[++ p] = i; //長度越界,第二關鍵字爲0
        for (i = 1; i <= n; i ++) if (sa[i] > w) y[++ p] = sa[i] - w;
        //由計數排序的操作過程可知,sa的前w個後綴在第二關鍵字中被移除,而後溢出的補0

        rsort();
        swap(y,rank);//上一輪的rank放到y中保存一下,用於計算下一輪的rank;

        rank[sa[1]]=p=1;
        for(i=2; i<=n; i++)rank[sa[i]]=cmp(y,sa[i],sa[i-1],w)?p:++p;
        //通過觀察上一輪的rank,比較當前排名爲i和i-1的rank是否一樣,如果一樣則rank相等,否則新加一個rank

    }
    //計算lcp
    int k=0,j;
    for(int i=1; i<=n; height[rank[i++]]=k)
        for(k=(k?k-1:k),j=sa[rank[i]-1]; (i+k)<=n&&(j+k)<=n&&a[i+k]==a[j+k]; k++);
    //通過我們的結論h[i]>=h[i-1]-1;可知我們設k=h[i-1]-1;那麼下一次匹配時只需從 a[i+k]==a[j+k]開始比較

}

char str[maxn],str1[maxn];
int l1,l2;
void init() {
    scanf("%s %s",str,str1);
    l1=strlen(str);
    l2=strlen(str1);
    for(int i=1; i<=l1; i++) {
        a[i]=str[i-1];
    }
    a[l1+1]=128;
    for(int i=0; i<l2; i++) {
        a[i+l1+2]=str1[i];
    }
    n=l1+l2+1;  
}
int main() {
    int ans=0;
    init();
    suffix();

    for(int i=2; i<=n; i++) {
        if(ans<height[i]) {
            int x = min(sa[i], sa[i - 1]);
            int y = max(sa[i], sa[i - 1]);
            if((x<l1+1)&&(y>l1+1))
                ans=max(height[i],ans);
        }
    }
    printf("%d\n",ans);
    return 0;
}
發佈了45 篇原創文章 · 獲贊 27 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章