Sample Input
30 25 27 30 34 39 45 52 60 69 79 69 60 52 45 39 34 30 26 22 18 82 78 74 70 66 67 64 60 65 80 0
Sample Output
5
大致题意:求不可重叠最长重复子串
参考 国家集训队论文:算法合集之《后缀数组——处理字符串的有力工具》
先二分答案,把题目变成判定性问题:
判断是否存在两个长度为k的子串是相同的,且不重叠。
解决这个问题的关键还是利用height数组。
把排序后的后缀分成若干组,其中每组的后缀之间的height值都不小于k。
例如,字符串为“aabaaaab”,当k=2时,后缀分成了4组
判断是否存在两个长度为k的子串是相同的,且不重叠。
解决这个问题的关键还是利用height数组。
把排序后的后缀分成若干组,其中每组的后缀之间的height值都不小于k。
例如,字符串为“aabaaaab”,当k=2时,后缀分成了4组
然后对于每组后缀,只须判断每个后缀的sa值的最大值和最小值之差是否不小于k。
如果有一组满足,则说明存在,否则不存在。整个做法的时间复杂度为O(nlogn)。
本题中利用height值对后缀进行分组的方法很常用,请读者认真体会。
需要注意的地方,字符串预处理:令 a[i]=a[i+1]-a[i]+size,与a[i]=a[i]-a[i-1]+size 相比,一个掐头,一个去尾,显然去尾是比较理智的;另外最后结果要+1.
由于最长匹配为n/2,合法长度需>4,所以可从[4,n/2]区间开始二分。
CODE:
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <stack>
#include <queue>
#include <map>
#include <set>
#include <vector>
#include <math.h>
#include <bitset>
#include <algorithm>
#include <climits>
using namespace std;
#define MAXN 200010
int t1[MAXN],t2[MAXN],c[MAXN];
int sa[MAXN],rank[MAXN],height[MAXN];
int r[MAXN];
char s1[MAXN],s2[MAXN];
int s[MAXN];
bool cmp(int *r, int a, int b,int l)
{
return r[a]==r[b]&&r[a+l]==r[b+l];
}
void DA(int str[], int sa[], int rank[], int height[], int n, int m)
{
// n++;
int i,j,p,*x=t1,*y=t2;
for(i=0; i<m; ++i)c[i]=0;
for(i=0; i<n; ++i)c[x[i]=str[i]]++;
for(i=1; i<m; ++i)c[i]+=c[i-1];
for(i=n-1; i>=0; --i)sa[ --c[ x[i] ] ]=i;
for(j=1; j<=n; j<<=1)
{
p=0;
for(i=n-j; i<n; ++i)y[p++]=i;
for(i=0; i<n; ++i)if(sa[i]>=j)y[p++]=sa[i]-j;
for(i=0; i<m; ++i)c[i]=0;
for(i=0; i<n; ++i)c[x[y[i]]]++;
for(i=1; i<m; ++i)c[i]+=c[i-1];
for(i=n-1; i>=0; --i)sa[--c[x[y[i]]]]=y[i];
swap(x,y);
p=1;
x[sa[0]]=0;
for(i=1; i<n; ++i)
x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
//if(p>=n)break;
m=p;
}
int k=0;
n--;
for(i=0; i<=n; ++i)rank[sa[i]]=i;
for(i=0; i<n; ++i)
{
if(k)--k;
j=sa[rank[i]-1];
while(str[i+k]==str[j+k])k++;
height[rank[i]]=k;
}
}
bool find_k(int k,int n)
{
int a,b,i;
a=b=sa[1];
for(i=2; i<=n; ++i)
{
if(height[i]<k)
a=b=sa[i];
else
{
a=min(a,sa[i]);
b=max(b,sa[i]);
if(b-a>=k)return 1;
}
}
return 0;
}
int a[20005];
int main()
{
int n;
while(~scanf("%d",&n))
{
if(!n)break;
int k,j=0;
for(int i=0; i<n; ++i)
scanf("%d",&a[i]);
for(int i=0; i<n-1; ++i)
a[i]=a[i+1]-a[i]+100;
a[n-1]=0;
DA(a,sa,rank,height,n,200);
int a=4,b=n>>1,mid;
int ans=0;
while(a<=b)
{
mid=(a+b)>>1;
if(find_k(mid,n))
{
ans=mid;
a=mid+1;
}
else b=mid-1;
}
if(ans>=4)printf("%d\n",ans+1);
else printf("0\n");
}
return 0;
}