题目链接:https://www.luogu.org/problemnew/show/P4051
思路:我们联想求后缀数组sa的过程,发现我们在求y数组的时候(第二关键字,下标为第二关键字的排位,值为合并之后关键字的位置),对于那些没有第二关键字的部分,我们都是直接补0,让这部分的第二关键字排在最前面,但是因为这道题是一个环,所以不存在没有第二关键字的情况,所以我们只需要在板子上面改点东西(求第二关键字的部分改一下),用取模来把范围控制在1到n之间就可以了。
这里因为可能有空格,所以我用gets来读取字符串了,把初始化为256,保险。
代码:
#include<iostream> #include<cstring> #include<algorithm> #include<queue> #include<map> #include<stack> #include<cmath> #include<vector> #include<set> #include<cstdio> #include<string> #include<deque> using namespace std; typedef long long LL; #define eps 1e-8 #define INF 0x3f3f3f3f #define maxn 100005 int c[maxn],x[maxn],y[maxn],sa[maxn]; int n,m,k,t; char s[maxn]; void output(int n){ for(int i=1;i<=n;i++){ int index=(sa[i]+n-2)%n+1;//因为sa的下标表示排位,值表示位置,输出的那个末尾字符就是sa[i]+n-1 //但是我们要控制范围,所以我们对n取模,取模可能为0,所以加1, //然后因为外面加了1,所以我们在括号里面减1,所以就是n-2了 printf("%c",s[index]); } printf("\n"); } void build_sa(int n){ m=256; for(int i=1;i<=m;i++) c[i]=0; for(int i=1;i<=n;i++) c[x[i]=s[i]]++; for(int i=2;i<=m;i++) c[i]+=c[i-1]; for(int i=n;i>=1;i--) sa[c[x[i]]--]=i; for(int k=1;k<=n;k<<=1){ int num=0; //去掉了补0的那部分,因为都存在第二关键字 for(int i=1;i<=n;i++) y[++num]=(sa[i]-k+n-1)%n+1;//我是下标从1开始,所以用取模操作把范围控制一下 // 对n取模加1,在括号里面减1,这样就抵消了 for(int i=1;i<=m;i++) c[i]=0; for(int i=1;i<=n;i++) c[x[i]]++; for(int i=2;i<=m;i++) c[i]+=c[i-1]; for(int i=n;i>=1;i--) sa[c[x[y[i]]]--]=y[i],y[i]=0; swap(x,y); num=1,x[sa[1]]=1; for(int i=2;i<=n;i++) x[sa[i]]=y[sa[i]]==y[sa[i-1]]&&y[(sa[i]+k-1)%n+1]==y[(sa[i-1]+k-1)%n+1]?num:++num; if(num>=n) break; m=num; } output(n); } int main() { while(gets(s+1)){ int len=strlen(s+1); build_sa(len); } return 0; }