链接
https://vjudge.net/problem/UVA-1314
题解
把整个字符串
c
o
p
y
copy
copy一份到末尾,然后对所有后缀排序
输出开头位置小于
n
n
n且字典序最小的后缀的序号
要在末尾放一个字典序很大的字符,原因如下:
理论上我们只想比较每个后缀的前
n
n
n个字符
但是如果最长公共前缀大于等于
n
n
n,那么就会出现一些问题
第
n
+
1
n+1
n+1个字符就会被比较,并且会影响到这两个后缀的顺序
但是想一想,既然我
s
h
i
f
t
shift
shift一定的距离之后这个串和自身重合,那么它一定是循环串,而且我
s
h
i
f
t
shift
shift的距离肯定是最小正周期的正整数倍
那么这两个串的比较肯定会一直进行到某个串的末尾
我是想保留长度尽量大的那个
所以我应该在串的末尾放一个字典序很大的字符
代码
#include <bits/stdc++.h>
#define maxn 200010
#define cl(x) memset(x,0,sizeof(x))
using namespace std;
struct SuffixArray
{
int sa[maxn], rank[maxn], ws[maxn], wv[maxn], wa[maxn], wb[maxn], height[maxn], N;
bool cmp(int *r, int a, int b, int l){return r[a]==r[b] and r[a+l]==r[b+l];}
void clear()
{
cl(sa), cl(rank), cl(ws), cl(wv), cl(wa), cl(wb), cl(height);
}
void build(int *r, int n, int m)
{
N=n;
n++;
int i, j, k=0, p, *x=wa, *y=wb, *t;
for(i=0;i<m;i++)ws[i]=0;
for(i=0;i<n;i++)ws[x[i]=r[i]]++;
for(i=1;i<m;i++)ws[i]+=ws[i-1];
for(i=n-1;i>=0;i--)sa[--ws[x[i]]]=i;
for(p=j=1;p<n;j<<=1,m=p)
{
for(p=0,i=n-j;i<n;i++)y[p++]=i;
for(i=0;i<n;i++)if(sa[i]>=j)y[p++]=sa[i]-j;
for(i=0;i<n;i++)wv[i]=x[y[i]];
for(i=0;i<m;i++)ws[i]=0;
for(i=0;i<n;i++)ws[wv[i]]++;
for(i=1;i<m;i++)ws[i]+=ws[i-1];
for(i=n-1;i>=0;i--)sa[--ws[wv[i]]]=y[i];
for(t=x,x=y,y=t,p=1,i=1,x[sa[0]]=0;i<n;i++)
x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
}
for(i=0;i<n;i++)rank[sa[i]]=i;
for(i=0;i<n-1;height[rank[i++]]=k)
for(k?k--:0,j=sa[rank[i]-1];r[i+k]==r[j+k];k++);
}
}SA;
int read(int x=0)
{
int c, f(1);
for(c=getchar();!isdigit(c);c=getchar())if(c=='-')f=-f;
for(;isdigit(c);c=getchar())x=x*10+c-48;
return f*x;
}
char s[maxn];
int r[maxn];
int main()
{
int T(read()), n, i;
while(T--)
{
n=read();
scanf("%s",s);
cl(r);
SA.clear();
for(i=0;i<n;i++)r[i]=s[i];
for(i=0;i<n;i++)r[n+i]=s[i];
r[2*n]=260;
SA.build(r,2*n+1,300);
for(i=0;i<=2*n+1;i++)
{
if(SA.sa[i]<n)
{
printf("%d\n",SA.sa[i]);
break;
}
}
}
return 0;
}