[后缀数组] POJ 1743 Musical Theme
题目大意
给定一串数字,求两个最长的变化幅度相同的不重叠的子串长度。
因为要求变化幅度相同,所以首先进行一次差分,然后实际上是求不重叠最长重复子串。
用后缀数组求出SA[] 和Height[] ,因为不能重叠,我们去二分不重叠重复子串的长度 \(k\),对于每个 \(k\),把Height数组中相邻的大于等于 \(k\) 的分为一组,对于每组求出SA的最大值和最小值,表示两个前缀相同的后缀开始位置的间距,这道题因为做了差分,所以要满足大于 \(k\) ,而不是大于等于。
时间复杂度 \(O(N\log N)\)。
Code
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <cstdio>
#include <vector>
using namespace std;
#define RG register int
#define LL long long
template<typename elemType>
inline void Read(elemType &T){
elemType X=0,w=0; char ch=0;
while(!isdigit(ch)) {w|=ch=='-';ch=getchar();}
while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
T=(w?-X:X);
}
const int maxn=20010;
const int INF=1<<30;
struct Suffix_Array{
int str[maxn];
int SA[maxn],Rank[maxn],B[maxn],x[maxn],y[maxn],Height[maxn];
int LenS,Base;
Suffix_Array():Base(176){}
void clear(){
memset(B,0,sizeof(B));
Base=176;
}
void Get_SA(){
clear();
int *X=x,*Y=y;
for(RG i=1;i<=LenS;++i) ++B[X[i]=str[i]];
for(RG i=2;i<=Base;++i) B[i]+=B[i-1];
for(RG i=1;i<=LenS;++i) SA[B[X[i]]--]=i;
for(RG k=1;k<=LenS;k<<=1){
RG Index=0;
for(RG i=LenS-k+1;i<=LenS;++i) Y[++Index]=i;
for(RG i=1;i<=LenS;++i) if(SA[i]>k) Y[++Index]=SA[i]-k;
for(RG i=1;i<=Base;++i) B[i]=0;
for(RG i=1;i<=LenS;++i) ++B[X[i]];
for(RG i=2;i<=Base;++i) B[i]+=B[i-1];
for(RG i=LenS;i>=1;--i) {SA[B[X[Y[i]]]--]=Y[i];Y[i]=0;}
swap(X,Y);
X[SA[1]]=1;Index=1;
for(RG i=2;i<=LenS;++i)
X[SA[i]]=(Y[SA[i-1]]==Y[SA[i]] && Y[SA[i-1]+k]==Y[SA[i]+k])?Index:++Index;
if(Index==LenS) break;
Base=Index;
}
return;
}
void Get_LCP(){
RG k=0;
for(RG i=1;i<=LenS;++i) Rank[SA[i]]=i;
for(RG i=1;i<=LenS;++i){
if(Rank[i]==1) continue;
if(k) --k;
RG j=SA[Rank[i]-1];
while(i+k<=LenS && j+k<=LenS && str[i+k]==str[j+k]) ++k;
Height[Rank[i]]=k;
}
return;
}
};
Suffix_Array SA;
int Data[maxn];
int N;
bool Judge(int k){
int Max,Min;
Max=Min=SA.SA[1];
for(int i=2;i<=N;++i){
if(SA.Height[i]>=k){
Min=min(Min,SA.SA[i]);
Max=max(Max,SA.SA[i]);
continue;
}
if(Max-Min>k) return true;
Max=Min=SA.SA[i];
}
if(SA.Height[N]>=k && Max-Min>k) return true;
return false;
}
int Solve(){
int L=0,R=N,Res=0;
while(L<=R){
int mid=(L+R)>>1;
if(Judge(mid)){Res=mid;L=mid+1;}
else R=mid-1;
}
return Res;
}
int main(){
while(~scanf("%d",&N)){
if(N==0) break;
for(RG i=1;i<=N;++i)
Read(Data[i]);
for(RG i=1;i<N;++i)
SA.str[i]=Data[i+1]-Data[i]+88;
SA.str[N]=0;
SA.LenS=--N;
SA.Get_SA();
SA.Get_LCP();
int Ans=Solve()+1;
if(Ans<5) Ans=0;
printf("%d\n",Ans);
}
return 0;
}