后缀数组sa(x)表示排序后第x位在排序前的位置。
这个东西的求法有两种,一种是倍增,时间复杂度o(n log n)或o(n log2n),另一种是用不知道什么方法做到的o(n)。
至于第二种方法是什么,并不对劲的人并不知道,所以只说倍增。
考虑正常地比较两个字符串,都是从头比较到尾:
那么,如果把两个字符串都断成两半,并且已知每一段的排名,就相当于以第一段为第一关键字,第二段为第二关键字排序了。
根据这个性质,就能想到如果先把字符串的每个位置开始长度为一的子串进行排序后,就能在至多n log n的时间内将每个位置开始长度为二的子串排序。
↑大概长这样,注意最后要补一个空字符。
以此类推,就能这样倍增地求出后缀的排序了,还是要注意最后补空字符。
如果用基数排序,每次排序的时间复杂度是o(n)的,那么总复杂度就是o(n log n)了。
如果用快速排序,总复杂度就是o(n log2n),心中有党常数极小才能过。

#include<iostream> #include<iomanip> #include<cmath> #include<cstring> #include<cstdio> #include<cstdlib> #include<algorithm> #include<queue> #define maxn 2000010 using namespace std; inline int read() { int xx=0,ff=1; char ch=getchar(); while(isdigit(ch)==0&&ch!=‘-‘)ch=getchar(); if(ch==‘-‘)ff=-1,ch=getchar(); while(isdigit(ch))xx=xx*10+ch-‘0‘,ch=getchar(); return xx*ff; } void write(int x) { int ff=0;char ch[15]; if(x<0) { x=-x; putchar(‘-‘); } while(x)ch[++ff]=(x%10)+‘0‘,x/=10; if(ff==0)putchar(‘0‘); while(ff)putchar(ch[ff--]); putchar(‘ ‘); } struct SA { int sa[maxn],ord[maxn],x[maxn],n,m; int y[maxn],c[maxn]; char s[maxn]; void start() { scanf("%s",s); n=strlen(s);m=130; } void s_sort() { memset(x,0,sizeof(x)); memset(y,0,sizeof(y)); memset(c,0,sizeof(c)); 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; for (int k=1;k<=n;k<<=1) { int p = 0; for (int i=n-k;i<n;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[y[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); p = 1; x[sa[0]] = 0; for (int i=1;i<n;i++) x[sa[i]]= (y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+k]==y[sa[i]+k])?p-1:p++; if (p>=n) break; m=p; } } void print() { for(int i=0;i<n;i++) write(sa[i]+1); } }t; int main() { t.start(); t.s_sort(); t.print(); return 0; }

#include<iostream> #include<iomanip> #include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<algorithm> #define maxn 1000010 using namespace std; int read() { int ff=1,x=0;char ch=getchar(); while(isdigit(ch)==0 && ch!=‘-‘)ch=getchar(); if(ch==‘-‘)ff=-1,ch=getchar(); while(isdigit(ch))x=x*10+ch-‘0‘,ch=getchar(); return x*ff; } void write(int x) { int ff=0;char ch[15]; while(x)ch[++ff]=(x%10)+‘0‘,x/=10; while(ff)putchar(ch[ff--]); putchar(‘ ‘); } char s[maxn]; bool f[maxn]; int rnk[maxn*2],ord[maxn*2],tp[maxn*2],n,c;//tmp(x)排在x位的是几号数,ord(x)第x号数排在第几位 void init() { scanf("%s",s); n=strlen(s); for(int i=1;i<=n;i++) ord[i]=int(s[i-1]),rnk[i]=i; for(int i=n+1;i<=n*2;i++) ord[i]=0; } bool cmp(int a,int b) { return ord[a]==ord[b]?ord[a+c]<ord[b+c]:ord[a]<ord[b]; } void print() { for(int i=1;i<=n;i++) { write(rnk[i]); } printf("\n"); } void cpy() { for(int i=1;i<=n;i++) { ord[i]=tp[i]; } } void s_sort() { int yes=0;c=0; do { sort(rnk+1,rnk+n+1,cmp);int tmp=1; // print(); for(int i=1;i<=n;i++) { tp[rnk[i]]=tmp;yes=tmp; tmp+=(ord[rnk[i]]==ord[rnk[i+1]]&&ord[rnk[i]+c]==ord[rnk[i+1]+c])?0:1; } cpy(); if(c!=0)c*=2; else c=1; }while(yes<n) ; } int main() { init(); s_sort(); print(); return 0; }