正题
题目链接:https://www.luogu.com.cn/problem/P3809
题目大意
长度为 的字符串,求它的字符数组(后缀排序后排名为 的在哪个位置)。
解题思路
大概思路就是倍增排序,先排每个后缀的第一个字符,然后是两个,然后是四个,以此类推。
为什么可以这样?我们由 的排列拓展到 时我们将其分为双关键字排序,第 位的第一关键字是第 位的 名次,第二关键字是第 位的 名次。就是说我们可以 得出第二关键字。
然后基数排序时间复杂度
(带注释版)
#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;
}
(无注释版)
#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;
}