后缀数组就是把一个字符串的后缀排序,然后就可以再乱搞一些东东。
DC3看着就累,还是倍增好。
当初看代码觉得虽然不长但是理解起来并不容易,各种数组有各自的意义很容易搞混。
特别是要理解基数排序。每次按第二关键字排序再按第一关键字排序,可以保证在第一关键字相同时第二关键字的大小关系也正确。
白书上的数组有s(字符串)、sa、x、y、c,计算height还用到了rank。
我的代码是照着白书上的写的(发现白书上的代码有几个小的细节问题)。
//Serene
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<cmath>
using namespace std;
const int maxn=1e6+10;
char s[maxn];
int n,m,p;
int sa[maxn],x[maxn],y[maxn],c[maxn];
//sa当前排好的后缀 x每个后缀的rank y由第二关键字排好的后缀 c记录每种相同的个数及前缀和
int height[maxn];
int main() {
gets(s); n=strlen(s); m='z'+1;
for(int i=0;i<n;++i) c[x[i]=s[i]]++;
for(int i=1;i<m;++i) c[i]+=c[i-1];
for(int i=n-1;i>=0;--i) sa[--c[x[i]]]=i;//sa[c[w]-1]为放入目前rank为w的可放的最后一个位置
for(int k=1;k<=n;k<<=1) {
p=0;
for(int i=n-1;i>=n-k;--i) y[p++]=i;//把已经没有第二关键字的放在前
for(int i=0;i<n;++i) if(sa[i]>=k) y[p++]=sa[i]-k;//根据第二关键字放入对应的第一关键字位置(后缀的起始位置)
for(int i=0;i<m;++i) c[i]=0;
for(int i=0;i<n;++i) c[x[i]]++;
for(int i=1;i<m;++i) c[i]+=c[i-1];//记录每个后缀第一关键字的信息
for(int i=n-1;i>=0;--i) sa[--c[x[y[i]]]]=y[i];//根据第一关键字排序
swap(x,y);//此时y已经无用,我们需要更新x数组必须用到曾经的x数组(判断相同的情况)
p=1; x[sa[0]]=0;
for(int i=1;i<n;++i) x[sa[i]]= y[sa[i-1]]==y[sa[i]]&&sa[i-1]+k<n&&sa[i]+k<n&&y[sa[i-1]+k]==y[sa[i]+k]? p-1:p++;
if(p>=n) break;
else m=p;
}
for(int i=0;i<p;++i) printf("%d ",sa[i]+1); printf("\n");
int k=0;
for(int i=0;i<n;++i) {
if(!x[i]) {
k=0; continue;
}
if(k) k--;
int j=sa[x[i]-1];
while(s[i+k]==s[j+k]) k++;
height[x[i]]=k;
}
for(int i=1;i<n;++i) printf("%d ",height[i]);
return 0;
}