正题
题目链接:https://www.luogu.com.cn/problem/P3809
题目大意
长度为nnn的字符串,求它的字符数组(后缀排序后排名为iii的在哪个位置)。
解题思路
大概思路就是倍增排序,先排每个后缀的第一个字符,然后是两个,然后是四个,以此类推。
为什么可以这样?我们由kkk的排列拓展到k∗2k*2k∗2时我们将其分为双关键字排序,第iii位的第一关键字是第iii位的kkk名次,第二关键字是第i+ki+ki+k位的kkk名次。就是说我们可以O(1)O(1)O(1)得出第二关键字。
然后基数排序时间复杂度O(nlogn)O(n\log n)O(nlogn)
codecodecode(带注释版)
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1e6+10;
int n,m,sa[N],x[N],y[N],c[N];
char s[N];
void Qsort(){//基数排序for(int i=1;i<=m;i++)c[i]=0;for(int i=1;i<=n;i++)c[x[i]]++;for(int i=1;i<=m;i++)c[i]+=c[i-1];for(int i=n;i>=1;i--)sa[c[x[y[i]]]--]=y[i],y[i]=0;
}
void Get_SA(char *s)
{for(int i=1;i<=n;i++)//将第一位获取位第一关键字x[i]=s[i]-'0'+1,y[i]=i;Qsort();for(int w=1;w<=n;w<<=1){int p=0;for(int i=n-w+1;i<=n;i++)y[++p]=i;//将2*w位后已经没有的先丢到前面for(int i=1;i<=n;i++)//每个对应到其i-w的第二关键字if(sa[i]>w) y[++p]=sa[i]-w;Qsort();swap(x,y);//排序x[sa[1]]=p=1;for(int i=2;i<=n;i++)//确定新的排名并判断排序是否结束x[sa[i]]=(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+w]==y[sa[i-1]+w])?p:++p;if(p==n) break;m=p;}return;
}
int main()
{scanf("%s",s+1);n=strlen(s+1);m=100;Get_SA(s);for(int i=1;i<=n;i++)printf("%d ",sa[i]);return 0;
}
codecodecode(无注释版)
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1e6+10;
int n,m,sa[N],x[N],y[N],c[N];
char s[N];
void Qsort(){for(int i=1;i<=m;i++)c[i]=0;for(int i=1;i<=n;i++)c[x[i]]++;for(int i=1;i<=m;i++)c[i]+=c[i-1];for(int i=n;i>=1;i--)sa[c[x[y[i]]]--]=y[i],y[i]=0;
}
void Get_SA(char *s)
{for(int i=1;i<=n;i++)x[i]=s[i]-'0'+1,y[i]=i;Qsort();for(int w=1;w<=n;w<<=1){int p=0;for(int i=n-w+1;i<=n;i++)y[++p]=i;for(int i=1;i<=n;i++)if(sa[i]>w) y[++p]=sa[i]-w;Qsort();swap(x,y);x[sa[1]]=p=1;for(int i=2;i<=n;i++)x[sa[i]]=(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+w]==y[sa[i-1]+w])?p:++p;if(p==n) break;m=p;}return;
}
int main()
{scanf("%s",s+1);n=strlen(s+1);m=100;Get_SA(s);for(int i=1;i<=n;i++)printf("%d ",sa[i]);return 0;
}