您的位置:首页 > 其它

后缀数组(不可重叠最长重复子串)

2016-12-14 21:29 369 查看
poj 1743

二分答案,把题目变成判定性问题:判断是否

存在两个长度为k的子串是相同的,且不重叠。解决这个问题的关键还是利用

height数组。把排序后的后缀分成若干组,其中每组的后缀之间的height值都

不小于k。

有希望成为最长公共前缀不小于k的两个后缀一定在同一组。然

后对于每组后缀,只须判断每个后缀的sa值的最大值和最小值之差是否不小于

k。如果有一组满足,则说明存在,否则不存在。

#include<cstdio>//sa为后缀数组,把后缀从小到大排序把后缀开头存起来,rank为名次数组,以i开头的后缀在所有后缀中排第几
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#define F(x) ((x)/3+((x)%3==1?0:tb))
#define G(x) ((x)<tb?(x)*3+1:((x)-tb)*3+2)
using namespace std;
const int maxn=1e6+10;
int wa[maxn],wb[maxn],ww[maxn],wv[maxn],n;
int cmp(int *r,int a,int b,int l)
{
return r[a]==r[b]&&r[a+l]==r[b+l];
}
void da(int *r,int *sa,int n,int m)
{
int i,j,p,*x=wa,*y=wb,*t;
for(i=0; i<m; i++) ww[i]=0;
for(i=0; i<n; i++) ww[x[i]=r[i]]++;
for(i=1; i<m; i++) ww[i]+=ww[i-1];
for(i=n-1; i>=0; i--) sa[--ww[x[i]]]=i; //处理长度为一的字符串,得到sa数组
for(j=1,p=1; p<n; j*=2,m=p) //倍增法求sa
{
for(p=0,i=n-j; i<n; i++) y[p++]=i;
for(i=0; i<n; i++) if(sa[i]>=j) y[p++]=sa[i]-j;//利用上次的sa直接求出按第二个关键字排序
for(i=0; i<n; i++) wv[i]=x[y[i]]; //第二关键字的排序得出第一关键字的顺序
for(i=0; i<m; i++) ww[i]=0;
for(i=0; i<n; i++) ww[wv[i]]++;
for(i=1; i<m; i++) ww[i]+=ww[i-1];
for(i=n-1; i>=0; i--) sa[--ww[wv[i]]]=y[i]; //根据第一关键字的顺序排出sa数组的顺序
for(t=x,x=y,y=t,p=1,x[sa[0]]=0,i=1; i<n; i++) //更新x数组 x为rank数组
x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
}
return ;
}
int h[maxn];//也就是排名相邻的两个后缀的最长公共前缀sa[i]和sa[i-1]
int Rank[maxn];//名次数组
void get_height(int *r,int *sa,int n)
{
int k=0,i,j;
for(int i=1; i<=n; i++) Rank[sa[i]]=i;
for(int i=0; i<n; h[Rank[i++]]=k)
for(k?k--:0,j=sa[Rank[i]-1]; r[i+k]==r[j+k]; k++) ;
return ;
}
int a[maxn];
int sa[maxn],r[maxn];
int judge(int mid)
{
int mn,mx,j;
for(int i=1; i<=n; i=j)
{
while(i<=n&&h[i]<mid)//排除最长前缀小于mid的
i++;
j=i;
mn=n;//记录同一组中sa的最小值
mx=0;//记录同一组中sa的最大值
if(i==n+1)
break;
mn=min(mn,sa[i-1]);
mx=max(mx,sa[i-1]);
while(j<=n&&h[j]>=mid)//大于mid的分在同一组
{
mn=min(mn,sa[j]);
mx=max(mx,sa[j]);
j++;
}
if(mx-mn>=mid)//如果不重复返回1
return 1;
}
return 0;
}
int main()
{
while(~scanf("%d",&n)&&n)
{
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]+89;
}
n--;
a
=0;
da(a,sa,n+1,180);
get_height(a,sa,n);
int l=0;
int R=n-1;
while(l<=R)//二分枚举长度
{
int mid=(l+R)>>1;
if(judge(mid))
l=mid+1;
else
R=mid-1;
}
int ans=R;
if(ans>=4)
printf("%d\n",ans+1);
else
printf("0\n");
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  poj