Problem
有一个长度为 n n n 的序列,我们称两个子序列是等价的,当且仅当其中一个子序列中所有的元素加 / / /减同一个数后与另一个子序列相同,如 1 , 2 , 4 1,2,4 1,2,4 和 5 , 6 , 8 5,6,8 5,6,8 等价。
我们称一个子序列是 m u s i c music music,当且仅当它满足一下条件:
- 它的长度至少是 5 5 5。
- 存在另一个字符串与它等价。
- 它和与它等价的字符串不能相交。
求出满足条件的子序列的最大长度,如无解输出 0 0 0。
数据规模: 1 ≤ n ≤ 20000 1\le n\le20000 1≤n≤20000, a i ∈ [   1 , 88   ] a_i\in[\,1,88\,] ai∈[1,88]。
Solution
题目大意是我自己 y y yy yy 出来的。(小声 b b bb bb)
我们注意到,对于两个等价的子序列,它们具体的值可能不同,但是元素间的相对大小是相同的。于是我们令 b i = a i − a i − 1 b_i=a_i-a_{i-1} bi=ai−ai−1,等价的子序列的 b i b_i bi 值肯定是一一对应相等的。
那么对于 b i b_i bi ,就是不可重叠最长重复子串模板,直接用后缀数组就可以了。
Code
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 20005
using namespace std;
int n,m,x[N],y[N],S[N],height[N],Rank[N],sa[N],sum[N];
void solve(){
int i,j;
for(i=1;i<=m;++i) sum[i]=0;
for(i=1;i<=n;++i) sum[x[i]=S[i]]++;
for(i=2;i<=m;++i) sum[i]+=sum[i-1];
for(i=n;i>=1;--i) sa[sum[x[i]]--]=i;
for(j=1;j<=n;j<<=1){
int num=0;
for(i=n-j+1;i<=n;++i) y[++num]=i;
for(i=1;i<=n;++i)
if(sa[i]>j) y[++num]=sa[i]-j;
for(i=1;i<=m;++i) sum[i]=0;
for(i=1;i<=n;++i) sum[x[i]]++;
for(i=2;i<=m;++i) sum[i]+=sum[i-1];
for(i=n;i>=1;--i) sa[sum[x[y[i]]]--]=y[i];
swap(x,y);
num=1,x[sa[1]]=1;
for(i=2;i<=n;++i)
x[sa[i]]=(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+j]==y[sa[i-1]+j])?num:++num;
if(n==num) break;
m=num;
}
}
void GetH(){
int i,k=0;
for(i=1;i<=n;++i) Rank[sa[i]]=i;
for(i=1;i<=n;++i){
if(Rank[i]==1) continue;
int j=sa[Rank[i]-1];
while(S[i+k]==S[j+k]) k++;
height[Rank[i]]=k;
if(k) k--;
}
}
bool check(int mid){
int Min,Max;
Min=Max=sa[1];
for(int i=2;i<=n;++i){
if(height[i]<mid)
Min=Max=sa[i];
else{
Min=min(Min,sa[i]);
Max=max(Max,sa[i]);
if(Max-Min>mid) return true;
}
}
return false;
}
int main(){
int i;
while(~scanf("%d",&n)){
if(!n) break;
for(i=1;i<=n;++i) scanf("%d",&S[i]);
for(i=1;i<n;++i) S[i]=S[i+1]-S[i]+100;
m=200,solve(),GetH();
int l=1,r=n;
while(l<r){
int mid=(l+r+1)>>1;
if(check(mid)) l=mid;
else r=mid-1;
}
printf("%d\n",l<4?0:l+1);
}
return 0;
}