智商餘額又不足了, 看着後綴樹的代碼看了一整天還是一知半解,看來暫時只能套模板了。
後綴數組sa[] 保存了一個字符串的所有後綴, 並且按字典序排序。 sa[i] =k 的意思就是 排在第2i個的後綴, 在原串裏是以k開頭的後綴。
rank[]數組是sa數組的逆運算, rank[k]=i的意思是從位置k開始的後綴在後綴數組中排第i位。
height數組保存了 後綴數組中相鄰兩個後綴的最大公共前綴, height[i] 的值是 sa[i-1]和sa[i] 的公共前綴長度。
我們要把一個串建立成後綴數組,首先要在這個傳的末尾加一個特殊的字符,保證這個字符沒有出現過且它的值比前面的字符都要小。 (這樣方便兩個後綴大瀟的比較)。
建立後綴數組採用倍增的方式, 我們需要知道以下先驗知識:
#include<stdio.h>
#include<string.h>
#include<iostream>
using namespace std;
#define maxn 20010
#define ws wss
int wa[maxn],wb[maxn],wv[maxn],ws[maxn];
int cmp(int *r,int a,int b,int l)
{return r[a]==r[b]&&r[a+l]==r[b+l];}
void da(int *r,int *sa,int n,int m)
{
int i,j,p,*x=wa,*y=wb,*t;
for(i=0;i<m;i++) ws[i]=0;
for(i=0;i<n;i++) ws[x[i]=r[i]]++;
for(i=1;i<m;i++) ws[i]+=ws[i-1];
for(i=n-1;i>=0;i--) sa[--ws[x[i]]]=i;
//上面四行就是把長度爲1的前綴按基數排序的過程 , 可以在紙上走一下這個過程幫助理解
for(j=1,p=1;p<n;j*=2,m=p) //j是前綴長度, p是不同大小的後綴數量, m爲大於所有值的值
{
for(p=0,i=n-j;i<n;i++) y[p++]=i;
for(i=0;i<n;i++)
if(sa[i]>=j)
y[p++]=sa[i]-j;
// 上面代碼使y保存了以第二關鍵字排序的結果 ,以第二關鍵字排序結果可利用上一個sa數組 , 在草紙上將上述過程模擬一遍即可理解
for(i=0;i<n;i++) wv[i]=x[y[i]];
for(i=0;i<m;i++) ws[i]=0;
for(i=0;i<n;i++) ws[wv[i]]++;
for(i=1;i<m;i++) ws[i]+=ws[i-1];
for(i=n-1;i>=0;i--) sa[--ws[wv[i]]]=y[i];
//以第一關鍵字排序
for(t=x,x=y,y=t,p=1,x[sa[0]]=0,i=1;i<n;i++) //交換x和y,rank值保存在x數組中,p爲不同串的個數
x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
}
return;
}
int rank[maxn],height[maxn];
void calheight(int *r,int *sa,int n) //求height過程
{
int i,j,k=0;
for(i=1;i<=n;i++) rank[sa[i]]=i;
for(i=0;i<n;height[rank[i++]]=k)
for(k?k--:0,j=sa[rank[i]-1];r[i+k]==r[j+k];k++);
return;
}
int main()
{
int sa[20010];//,note[20010];
int n,i,j,k;
int t1,t2;
n=5;
int note[6]={2,2,3,2,2,0};
da(note,sa,n+1,4);
calheight(note,sa,n);
for(i=1;i<=n;i++)
cout<<sa[i]<<endl;
cout<<".."<<endl;
for(j=1;j<=n;j++)
cout<<height[j]<<endl;
// }
return 0;
}
整個過程大致就是上面這樣,有的細節還需要再理解。 利用後綴數組解決具體問題還得再通過實踐鍛鍊。