[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;
}
posted @ 2020-08-12 13:49  kakakakakaka  阅读(96)  评论(0编辑  收藏  举报

Never forget why you start

//鼠标爆炸特效