算法分析
这题相当于对环形字符串进行后缀排序,处理环上问题的一般策略是破环为链
,将原字符串复制一倍接在原字符串后面,然后对这个新字符串进行后缀排序。
为什么这个算法是对的?后缀中\(>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;
}