您的位置:首页 > 其它

【后缀数组】不可重叠最长重复子串

2017-09-26 18:42 232 查看
hz2016评测《《点击访问

caioj《《点击访问

这题阴森可怕,两个后缀可以不相同?哦天哪,怎么判断呢。

于是,某个聪明的人发现,后缀中两两字符的ANSI值的差是相等的,于是我们可以维护一个由差值组成的后缀数组。所以呢,我们就可以用后缀数组解这道题了,但是这一题,我们还要引入一个新的概念height数组和h数组。

height数组的定义就是排名为 i 与 i-1 的最长公共前缀。

h就是代表排名为 i  的后缀与其前一个后缀的最长前缀
#include<map>
#include<queue>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#define Maxchar 28192 //20000 +2^13
#define Maxs 30
#define mes(x,y) memset(x,y,sizeof(x));
#define mpy(x,y) memcpy(x,y,sizeof(x))
#define INF 2147483647
using namespace std;
int n,a[Maxchar+1],tt[Maxchar+1],Rank[Maxchar+1],sa1[Maxchar+1],Rank2[Maxchar+1],sa2[Maxchar+1],Rsort[Maxchar+1];
void get_sa(int n,int m){
memcpy(Rank,a,sizeof(Rank));
memset(Rsort,0,sizeof(Rsort));
for(int i=1;i<=n;i++)Rsort[Rank[i]]++;
for(int i=1;i<=m;i++)Rsort[i]+=Rsort[i-1];
for(int i=n;i>=1;i--)sa1[Rsort[Rank[i]]--]=i;
int ln=1,p=0;
while(p<n){
int k=0;
for(int i=n-ln+1;i<=n;i++)sa2[++k]=i;
for(int i=1;i<=n;i++)if(sa1[i]-ln>0)sa2[++k]=sa1[i]-ln;
memset(Rsort,0,sizeof(Rsort));
for(int i=1;i<=n;i++)Rsort[Rank[i]]++;
for(int i=1;i<=m;i++)Rsort[i]+=Rsort[i-1];
for(int i=n;i>=1;i--)sa1[Rsort[Rank[sa2[i]]]--]=sa2[i];
for(int i=1;i<=n;i++)tt[i]=Rank[i];
p=1;Rank[sa1[1]]=1;
for(int i=2;i<=n;i++){
if(tt[sa1[i]]!=tt[sa1[i-1]]||tt[sa1[i]+ln]!=tt[sa1[i-1]+ln])p++;
Rank[sa1[i]]=p;
}
m=p;ln*=2;
}
}
//上面是基础的后缀数组模版。
int height[Maxchar+1];
void get_he(int n){
int j,k=0;
for(int i=2;i<=n;i++){
j=sa1[Rank[i]-1];
if(k!=0)k--;
while(a[j+k]==a[i+k])k++;//匹配最初前缀
//h数组的定义就是后缀 i 和后缀 i-1的最长前缀
height[Rank[i]]=k;//记录两个后缀的最长前缀
//最后记录下排名为 i 的后缀与其前一个后缀的最长前缀
}
}
//就是新的h数组,这个才是后缀数组的真谛。
bool check(int k,int n){
for(int i=2;i<=n;i++){
if(height[i]>=k){
for(int j=i-1;j>=2;j--){
if(abs(sa1[i]-sa1[j])>=k)return true;//满足直接true
if(height[j]<k)break;//二分假如现在条件不满足就跳出
}
}
}
return false;
}
int main(){
while(scanf("%d",&n)!=EOF){
if(n==0)break;
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
int mmax=-9999999;
for(int i=1;i<n;i++){
a[i]=a[i+1]-a[i]+88;
if(mmax<a[i])mmax=a[i];
//取maxx是为了减少后缀数组的基数排序时间复杂度
}
a
=0;n--;
get_sa(n,mmax);get_he(n);
//然后通过二分枚举答案
int l=1,r=n,ans=1;
while(l<=r){
int mid=(l+r)/2;
if(check(mid,n)==true){//二分枚举长度
ans=mid;
l=mid+1;
}
else r=mid-1;
}
if(ans<4)printf("0\n");
else printf("%d\n",ans+1);
}
return 0;
}

 

查看原文:http://hz2016.tk/blog/?p=29
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: