Luogu4051/BZOJ1031 [JSOI2007]字符加密

题目传送门

算法分析

这题相当于对环形字符串进行后缀排序,处理环上问题的一般策略是破环为链,将原字符串复制一倍接在原字符串后面,然后对这个新字符串进行后缀排序。

为什么这个算法是对的?后缀中\(>n\)那一部分字符串会不会影响答案?

因为我们只关注新的字符串后缀前\(n\)位,并且只有两个后缀满足\(n\)位完全相同时才会进行后面的比较,这种情况只会在形如aaaaa这种字符串中出现,显然这种字符串无论是什么排序顺序都不会影响结果。

注意空间开2倍!!!

代码实现

#include<bits/stdc++.h>
using namespace std;
#define maxn 200005//错误笔记:复制二倍数组没开够 
#define maxm 400005
char s[maxn];
int n;
int sa[2][maxn],rk[2][maxn],v[maxn];
int main(){
	scanf("%s",s+1);
	n=strlen(s+1);
	for(int i=1;i<=n;i++)s[i+n]=s[i];
	n=strlen(s+1);
	int p=0,q=1;
	for(int i=1;i<=n;i++)v[s[i]]++;
	for(int i=1;i<128;i++)v[i]+=v[i-1];
	for(int i=1;i<=n;i++)sa[0][v[s[i]]--]=i;
	for(int i=1;i<=n;i++)rk[0][sa[0][i]]=rk[0][sa[0][i-1]]+(s[sa[0][i-1]]!=s[sa[0][i]]);
	for(int k=1;k<=n;(k<<=1),swap(p,q)){
		for(int i=1;i<=n;i++)v[rk[p][sa[p][i]]]=i;
		for(int i=n;i;i--)
			if(sa[p][i]>k)sa[q][v[rk[p][sa[p][i]-k]]--]=sa[p][i]-k;
		for(int i=n-k+1;i<=n;i++)sa[q][v[rk[p][i]]--]=i;
		for(int i=1;i<=n;i++)rk[q][sa[q][i]]=rk[q][sa[q][i-1]]+((rk[p][sa[q][i]]!=rk[p][sa[q][i-1]])||(rk[p][sa[q][i]+k]!=rk[p][sa[q][i-1]+k]));
		if(rk[q][sa[q][n]]==n)break;
	}
	for(int i=1;i<=n;i++)
		if(sa[q][i]<=n/2)printf("%c",s[sa[q][i]+n/2-1]);
	return 0;
}
原文地址:https://www.cnblogs.com/ZigZagKmp/p/11479796.html