【题解】[JSOI2007]字符加密

Link

\(\text{Solution:}\)

后缀数组第一题祭……

观察一下,这个是让求一个环形的原字符串的后缀,我们可以考虑一下断环为链。

对于\(aba\)我们扩展成\(abaaba,\)则一个后缀\(baa\)对应的就是\(baaba,aba\)对应的就是\(abaaba\).

那么,两个扩展后的后缀如果要比大小,则要从前向后一个个比较。也就是说,对于原来环形串上的段,它们的排名其实是不变的。因为前一段包括这一段的部分是不变的,又因为它从前向后比较,所以相对排名不变。

那么,我们将原串扩展为两倍后,进行后缀排序。

对于已经知道的\(sa[i]\)我们从小到大枚举\(i\)(根据题意,排名从小到大),并且判断这个起点是不是可以包含一段对应的后缀。如果包含,我们就把它对应的那一段的最后一个字符输出即可。

后缀数组我们可以做到\(O(n\log n).\)

#include<bits/stdc++.h>
using namespace std;
const int MAXN=2e6+10;
char s[MAXN];
int rk[MAXN],sa[MAXN],o[MAXN<<1],n,m=200,px[MAXN],cnt[MAXN],id[MAXN];
inline bool cmp(int x,int y,int w){return o[x]==o[y]&&o[x+w]==o[y+w];}
#define D cout<<"qwq\n";
int main(){
	cin>>(s+1);
	n=strlen(s+1)<<1;
	//D
	for(int i=1;i<=(n/2);++i)s[i+(n/2)]=s[i];
//	for(int i=1;i<=n;++i)cout<<s[i];
//	cout<<endl;
	for(int i=1;i<=n;++i)++cnt[rk[i]=s[i]];
	for(int i=1;i<=m;++i)cnt[i]+=cnt[i-1];
	for(int i=n;i>=1;--i)sa[cnt[rk[i]]--]=i;
	int p,w;
	//D
	for(w=1;w<=n;w<<=1,m=p){
		p=0;
		for(int i=n;i>n-w;--i)id[++p]=i;
		for(int i=1;i<=n;++i)if(sa[i]>w)id[++p]=sa[i]-w;
		fill(cnt,cnt+m+1,0);
		for(int i=1;i<=n;++i)++cnt[px[i]=rk[id[i]]];
		for(int i=1;i<=m;++i)cnt[i]+=cnt[i-1];
		for(int i=n;i>=1;--i)sa[cnt[px[i]]--]=id[i];
		memcpy(o,rk,sizeof(rk));p=0;
		for(int i=1;i<=n;++i)rk[sa[i]]=cmp(sa[i],sa[i-1],w)?p:++p;
		if(m==n)break;
		//D
	}
//	for(int i=1;i<=n;++i)cout<<sa[i]<<" ";
//	cout<<endl;
	for(int i=1;i<=n;++i){
		if(sa[i]<=n/2)cout<<s[sa[i]+n/2-1];
	}
	//JSOI07JSOI07
	return 0;
}
posted @ 2020-05-10 10:26  Refined_heart  阅读(91)  评论(0编辑  收藏  举报