學習步驟
首先明確幾個概念:
sa[i]:即,排名爲i的後綴的起點下標
rank[i]:即,第i個後綴的排名
height[i]:即,sa[i]和sa[i-1]的最長公共前綴
h[i]:即,height[rank[i]],即第i個後綴與前一名的最長公共前綴
一些結論
結論一:
定義:
則轉化: ,sa[i]+1≤k≤sa[j]
結論二:
suffix[i]的與其他後綴的最長公共前綴爲
結論三:
≥ ,理由suffix[i-1]去掉一個字符後的suffix[i],必然存在另一個後綴也去掉相同字符後的的匹配長度爲h[i-1]-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;
}