【后缀数组】[POJ1743]Musical Theme 乐曲主旋律
题目:
后缀数组模板题,做法参见罗穗骞论文。
自认为代码写得比较好看。
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define MAXN 500000
#define MAXD 200
int array[4][MAXN*2+10],height[MAXN+10],ans;
int *nsa,*rk,*nrk,*sa;
int a[MAXN+10],n,b[MAXN+10];
void Read(int &x){
char c;
while(c=getchar(),c!=EOF)
if(c>='0'&&c<='9'){
x=c-'0';
while(c=getchar(),c>='0'&&c<='9')
x=x*10+c-'0';
ungetc(c,stdin);
return;
}
}
void init(){
Read(a[1]);
for(int i=2;i<=n;i++){
Read(a[i]);
a[i-1]=a[i]-a[i-1]+100;
}
a[n]=-1;
n--;
}
void cal_sa(){
int i,k;
sa=array[0],nsa=array[1],rk=array[2],nrk=array[3];
memset(b,0,sizeof b);
for(i=1;i<=n;i++)
b[a[i]]++;
for(i=1;i<=MAXD;i++)
b[i]+=b[i-1];
for(i=1;i<=n;i++)
sa[b[a[i]]--]=i;
for(i=1;i<=n;i++){
rk[sa[i]]=rk[sa[i-1]];
if(a[sa[i]]!=a[sa[i-1]])
rk[sa[i]]++;
}
for(k=1;k<n&&rk[sa[n]] < n;k<<=1){
for(i=1;i<=n;i++)
b[rk[sa[i]]]=i;
for(i=n;i;i--)
if(sa[i]>k)
nsa[b[rk[sa[i]-k]]--]=sa[i]-k;
for(i=n-k+1;i<=n;i++)
nsa[b[rk[i]]--]=i;
for(i=1;i<=n;i++){
nrk[nsa[i]]=nrk[nsa[i-1]];
if(rk[nsa[i]]!=rk[nsa[i-1]]||rk[nsa[i]+k]!=rk[nsa[i-1]+k])
nrk[nsa[i]]++;
}
swap(sa,nsa);
swap(rk,nrk);
}
}
void cal_height(){
int i,k=0,j;
for(i=1;i<=n;i++)
if(rk[i]==1)
height[rk[1]]=0;
else{
if(k>0)
k--;
for(j=sa[rk[i]-1];a[i+k]==a[j+k];k++);
height[rk[i]]=k;
}
}
bool check(int len){
int mx=sa[1],mi=sa[1],i;
for(i=2;i<=n;i++){
if(height[i]>=len)
mx=max(mx,sa[i]),mi=min(mi,sa[i]);
else{
if(mx-mi>len) //两个串+1后可能有重叠
return 1;
mx=mi=sa[i];
}
}
if(mx-mi>len)
return 1;
return 0;
}
void partition(int l,int r){
int mid;
while(l<=r){
mid=(l+r)>>1;
if(check(mid))
l=mid+1,ans=mid;
else
r=mid-1;
}
}
int main()
{
while(Read(n),n){
ans=-1;
init();
cal_sa();
cal_height();
partition(4,n);
printf("%d\n",ans+1);
}
}