POJ-1743 Musical Theme(后缀数组)
题目大意:给一个整数序列,找出最长的连续变化相同的、至少出现两次并且不相重叠一个子序列。
题目分析:二分枚举长度进行判定。
代码如下:
# include<iostream>
# include<cstdio>
# include<cstring>
# include<algorithm>
using namespace std;
# define mid (l+(r-l)/2)
const int N=20000;
int SA[N+5],height[N+5];
int *rk,*tSA;
int n,a[N+5],cnt[N+5];
bool same(int i,int j,int k)
{
if(tSA[i]!=tSA[j]) return false;
if(i+k>=n&&j+k>=n) return true;
if(i+k>=n&&j+k<n) return false;
if(i+k<n&&j+k>=n) return false;
return tSA[i+k]==tSA[j+k];
}
void buildSA()
{
int m=180;
for(int i=0;i<m;++i) cnt[i]=0;
for(int i=0;i<n;++i) ++cnt[rk[i]=a[i]];
for(int i=1;i<m;++i) cnt[i]+=cnt[i-1];
for(int i=n-1;i>=0;--i) SA[--cnt[rk[i]]]=i;
for(int k=1;k<n;k<<=1){
int p=0;
for(int i=n-k;i<n;++i) tSA[p++]=i;
for(int i=0;i<n;++i) if(SA[i]>=k) tSA[p++]=SA[i]-k;
for(int i=0;i<m;++i) cnt[i]=0;
for(int i=0;i<n;++i) ++cnt[rk[tSA[i]]];
for(int i=1;i<m;++i) cnt[i]+=cnt[i-1];
for(int i=n-1;i>=0;--i) SA[--cnt[rk[tSA[i]]]]=tSA[i];
swap(rk,tSA);
p=1;
rk[SA[0]]=0;
for(int i=1;i<n;++i){
if(same(SA[i],SA[i-1],k)) rk[SA[i]]=p-1;
else rk[SA[i]]=p++;
}
if(p>=n) break;
m=p;
}
}
void getHeight()
{
for(int i=0;i<n;++i) rk[SA[i]]=i;
int k=0;
height[0]=0;
for(int i=0;i<n;++i){
if(rk[i]==0) k=0;
else{
if(k) --k;
int j=SA[rk[i]-1];
while(i+k<n&&j+k<n&&a[i+k]==a[j+k]) ++k;
height[rk[i]]=k;
}
}
}
bool ok(int x)
{
int maxn=SA[0],minn=SA[0];
for(int i=1;i<n;++i){
if(minn+x<maxn) return true;
if(height[i]>=x){
maxn=max(maxn,SA[i]);
minn=min(minn,SA[i]);
}else{
maxn=minn=SA[i];
}
}
return minn+x<maxn;
}
void init()
{
rk=new int[n+5];
tSA=new int[n+5];
}
void destroy()
{
delete []rk;
delete []tSA;
}
int main()
{
while(scanf("%d",&n)&&n)
{
init();
for(int i=0;i<n;++i){
scanf("%d",a+i);
--a[i];
}
--n;
int temp=a[0];
for(int i=0;i<n;++i){
a[i]=a[i+1]-temp+87;
temp=a[i+1];
}
buildSA();
getHeight();
//二分枚举最短的不满足条件的
int l=4,r=n;
while(l<r){
if(ok(mid)) l=mid+1;
else r=mid;
}
printf("%d\n",l>=5?l:0);
destroy();
}
return 0;
}


浙公网安备 33010602011771号