[JSOI2007]字符加密
[JSOI2007]字符加密
\(Description:\)
给你一个长度位\(n\)的字符串,将它首尾相连结成环,然后分别断开每两个点,这样可以得到\(n\)个字符串,将这\(n\)个字符串从小到大排序后输出每个字符串的最后一位
\(Solution:\)
后缀数组的经典题,也称“寻找最小的循环移动位置”
倍长原字符串,跑一遍后缀数组,然后按顺序输出每一个后缀的最后一位即可
\(Code:\)
#include<bits/stdc++.h>
using namespace std;
const int N=100005;
const int M=255;
int n,m,sa[N*2],cnt[M+1],x[N*2],y[N*2];
char s[N*2];
void get_sa(){
for(int i=1;i<=n;i++)cnt[x[i]=s[i]]++;
for(int i=2;i<=m;i++)cnt[i]+=cnt[i-1];
for(int i=n;i>=1;i--)sa[cnt[x[i]]--]=i;
for(int w=1;w<=n;w<<=1){
int num=0;
for(int i=n-w+1;i<=n;i++)y[++num]=i;
for(int i=1;i<=n;i++)if(sa[i]>w)y[++num]=sa[i]-w;
for(int i=1;i<=m;i++)cnt[i]=0;
for(int i=1;i<=n;i++)cnt[x[i]]++;
for(int i=2;i<=m;i++)cnt[i]+=cnt[i-1];
for(int i=n;i>=1;i--)sa[cnt[x[y[i]]]--]=y[i],y[i]=0;
swap(x,y);x[sa[1]]=num=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])?num:++num;
if((m=num)==n)break;
}
}
int main(){
scanf("%s",s+1);
n=strlen(s+1);
for(int i=1;i<=n;i++)s[i+n]=s[i];
n<<=1;m=M;get_sa();
for(int i=1;i<=n;i++)
if(sa[i]<=(n>>1))putchar(s[sa[i]+(n>>1)-1]);
return 0;
}