题目大意:
给n个数组成的串,求是否有多个“相似”且间隔至少为1的子串的长度大于等于5,两个子串相似当且仅当长度相等且每一位的数字差都相等。
解题思路:
首先把问题转化成重复子串的问题:把原串每一位都与前一位相减。这样得出的新串如果有两个长度为n的子串相同,那么它们对应在原串的长度n+1的子串也就相似。
再求出后缀数组。
如果只是求可重叠重复最长子串,答案就是height的最大值。
但这道题要求重复子串间隔至少为1。
先二分答案,转化成判别式的问题比较好处理。假设当前需要判别长度为k是否符合要求,只需把排序后的后缀分成若干组,其中每组的后缀之间的height 值都不小于k,再判断其中有没有不重复的后缀,具体就是看最大的SA值和最小的SA值相差超不超过k,有一组大于k就是合法答案。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int getint()
{
int i=0,f=1;char c;
for(c=getchar();(c<'0'||c>'9')&&c!='-';c=getchar());
if(c=='-')f=-1,c=getchar();
for(;c>='0'&&c<='9';c=getchar())i=(i<<3)+(i<<1)+c-'0';
return i*f;
}
const int N=20005;
int n,m,a[N],rank[N],sa[N],tp[N],c[N],height[N];
void Rsort()
{
for(int i=1;i<=m;i++)c[i]=0;
for(int i=1;i<=n;i++)c[rank[tp[i]]]++;
for(int i=1;i<=m;i++)c[i]+=c[i-1];
for(int i=n;i;i--)sa[c[rank[tp[i]]]--]=tp[i];
}
void SA_init()
{
m=200;
for(int i=1;i<=n;i++)rank[i]=a[i],tp[i]=i;
Rsort();
for(int w=1;w<n;w<<=1)
{
int j=0;
for(int i=n-w+1;i<=n;i++)tp[++j]=i;
for(int i=1;i<=n;i++)if(sa[i]>w)tp[++j]=sa[i]-w;
Rsort();
for(int i=1;i<=n;i++)swap(rank[i],tp[i]);
rank[sa[1]]=j=1;
for(int i=2;i<=n;i++)rank[sa[i]]=(tp[sa[i]]==tp[sa[i-1]]&&tp[sa[i]+w]==tp[sa[i-1]+w]?j:++j);
m=j;
}
int w=0,j;
for(int i=1;i<=n;height[rank[i++]]=w)
for(w=w?w-1:w,j=sa[rank[i]-1];a[i+w]==a[j+w];w++);
}
bool check(int k)
{
int mi=n+1,mx=0;
for(int i=2;i<=n;i++)
{
if(height[i]>=k)
{
mi=min(mi,min(sa[i],sa[i-1]));
mx=max(mx,max(sa[i],sa[i-1]));
if(mx-mi>k)return true;
}
else mi=n+1,mx=0;
}
return false;
}
int main()
{
//freopen("lx.in","r",stdin);
while(n=getint())
{
for(int i=1;i<=n;i++)a[i]=getint();
n--;
for(int i=1;i<=n;i++)a[i]=a[i+1]-a[i]+100;
SA_init();
int l=0,r=n;
while(l<=r)
{
int mid=l+r>>1;
if(check(mid))l=mid+1;
else r=mid-1;
}
if(r>=4)printf("%d\n",r+1);
else puts("0");
}
return 0;
}