题目传送门
题目挺不错的。
赤果果的后缀数组啊。
我好像只会后缀数组作后缀排名了。
真的菜。(我要变强!!)
因为他的字符串排成了一圈了。所以说我们应该长度为两倍才行。
比如:
abcd。
那么就会有四个串。
abcd,bcda,cdab,dabc。
这样很容易就可以看出我们应该把字符串变成:
abcdabc。
这样的话我们就会有这四个串了。
那么拿新的字符串去跑一遍后缀数组。
那么就会有2*len-1个后缀。len表示原串长度
那么有些字符串很明显要越界了。
所以我们只要不越界的字符串输出最后一个字符就OK了。
比如这个后缀的初始位置为i。
因为每个串的长度都应该为len。
那么我们就要判断i+len-1也就是这个串的结束位置是否越界(<=2*len-1)
然后把最后一个字符都输出出来就OK了吧。
代码实现:
#include<cstdio>
#include<cstring>
using namespace std;
char s[1100000];
int sa1[1100000],sa2[1100000],Rsort[1100000],Rank[1100000],a[1100000],tt[1100000];
char ans[1110000];
void solve(int n,int m) {
for(int i=1;i<=n;i++)
Rank[i]=a[i];
memset(Rsort,0,sizeof(Rsort));
for(int i=1;i<=n;i++)
Rsort[Rank[i]]++;
for(int i=1;i<=m;i++)
Rsort[i]+=Rsort[i-1];
for(int i=n;i>=1;i--)
sa1[Rsort[Rank[i]]--]=i;
int p=0,ln=1;
while(p<n) {
int k=0;
for(int i=n-ln+1;i<=n;i++)
sa2[++k]=i;
for(int i=1;i<=n;i++)
if(sa1[i]-ln>0) sa2[++k]=sa1[i]-ln;
memset(Rsort,0,sizeof(Rsort));
for(int i=1;i<=n;i++)
Rsort[Rank[i]]++;
for(int i=1;i<=m;i++)
Rsort[i]+=Rsort[i-1];
for(int i=n;i>=1;i--)
sa1[Rsort[Rank[sa2[i]]]--]=sa2[i];
for(int i=1;i<=n;i++)
tt[i]=Rank[i];
p=1;
Rank[sa1[1]]=1;
for(int i=2;i<=n;i++) {
if(tt[sa1[i]]!=tt[sa1[i-1]] || tt[sa1[i]+ln]!=tt[sa1[i-1]+ln])
p++;
Rank[sa1[i]]=p;
}
m=p;
ln*=2;
}
int len=0;
for(int i=1;i<=n;i++)
if(sa1[i]+n/2<=n)
//这里本来写的是sa1[i]+n/2+1-1的,因为2*len-1很明显为基数,那么他除以2的话就会比原长度少1,那么要加上1,后面的-1就是加上长度之后-1的位置就是结尾。
ans[++len]=s[sa1[i]+n/2];
for(int i=1;i<=len;i++)
printf("%c",ans[i]);
printf("\n");
}
int main() {
scanf("%s",s+1);
int len=strlen(s+1);
for(int i=1;i<=len-1;i++) //把字符串复制一遍
s[i+len]=s[i];
len*=2;len--;
for(int i=1;i<=len;i++)
a[i]=s[i];
solve(len,300);
return 0;
}
后缀数组有点玄学,初二的时候第一次学很懵13。
然而现在终于理解了,太开心了!