后缀排序
挖个坑。SAM这种坑B玩意我寒假之前一定要搞懂。但现在而今眼目下,我的功力还不够,搞后缀自动机很有点困难,毕竟我连AC自动机都不会……
但后缀数组相对而言简单一些。学的时候并不知道有什么用,但后来发现太有趣了。首先入门题,后缀排序。
这里使用了两个东西,一个是基数排序,一个是倍增思想。基数排序可以理解为对一个二元组进行桶排,倍增思想不多说。整个排序过程就是对排序长度进行倍增的过程,不难想到它的复杂度是\(O(NlogN)\)的。实现上难度不大。
我的写法比较笨但比较清晰,只是跑得慢是慢了点。
#include<cstdio>
#include<cstring>
//#define zczc
using namespace std;
const int N=1000010;
inline void read(int &wh){
wh=0;int f=1;char w=getchar();while(w<'0'||w>'9'){if(w=='-')f=-1;w=getchar();}
while(w<='9'&&w>='0'){wh=wh*10+w-'0';w=getchar();}wh*=f;return;
}
inline int min(int s1,int s2){return s1<s2?s1:s2;}
inline int max(int s1,int s2){return s1<s2?s2:s1;}
char w[N];int ss[200],a[N],m,cnt;
struct node{int id,x,y;}s[N],r[N];
int maxn=200,sum[N],num[N],use[N];
void init(){for(int i=0;i<=maxn;i++)sum[i]=num[i]=use[i]=0;}
void work(){
for(int i=1;i<=m;i++)maxn=max(maxn,s[i].y),num[s[i].y]++;sum[0]=num[0];
for(int i=1;i<=maxn;i++)sum[i]=sum[i-1]+num[i];
for(int i=1;i<=m;i++)r[sum[s[i].y-1]+num[s[i].y]--]=s[i];init();
for(int i=1;i<=m;i++)maxn=max(maxn,s[i].x),num[s[i].x]++;
for(int i=1;i<=maxn;i++)sum[i]=sum[i-1]+num[i];
for(int i=1;i<=m;i++)s[sum[r[i].x-1]+(++use[r[i].x])]=r[i];init();
}
signed main(){
#ifdef zczc
freopen("in.txt","r",stdin);
#endif
for(int i='0';i<='9';i++)ss[i]=++cnt;for(int i='A';i<='Z';i++)ss[i]=++cnt;for(int i='a';i<='z';i++)ss[i]=++cnt;
scanf("%s",w);for(m=1;w[m-1]!='\0';m++)a[m]=ss[w[m-1]];m--;
for(int n=1;n<=m;n<<=1){
for(int i=1;i<=m;i++)s[i].id=i,s[i].x=a[i],s[i].y=i+n>m?0:a[i+n];work();maxn=0;
for(int i=1;i<=m;i++)a[s[i].id]=(i==1||s[i].x!=s[i-1].x||s[i].y!=s[i-1].y)?++maxn:maxn;
}
for(int i=1;i<=m;i++)printf("%d ",s[i].id);return 0;
}
一如既往,万事胜意