以下题目均来自罗穗骞的论文...
给定一个字符串,询问某两个后缀的最长公共前缀。
某两个后缀的最长公共前缀就是区间height最小值,转化为RMQ问题,nlgn预处理,O(1)询问...
给定一个字符串,求最长重复子串,这两个子串可以重叠。
就是height数组的最大值...
给定一个字符串,求最长重复子串,这两个子串不能重叠。
这道题有一个特殊要求是如果一个子串的每个数同时加上一个相同的数字所得到的子串也在字符串中出现的话也是重复子串...
我们先忽略特殊要求只考虑普通的重复子串...
首先我们通过二分答案把最优性问题转化为可行性问题...
我们二分最长的重复字串的长度为len,然后我们根据len把sa按照height数组分组,我们把sa分成若干组,保证每一组的height最小值都大于等于len,这样就保证了组内的最长公共前缀是大于等于len的,现在要求不重复,就是要求最长公共前缀大于等于len的两个后缀的开头距离是大于等于len的,这样就保证了不重复...
现在考虑特殊要求,因为整体加数相同的话说,差分之后也应该相同,所以我们把n个数通过差分变成n-1个数,其他的照旧,只不过开头距离大于len的才合法...(因为是差分之后的...)
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
//by NeighThorn
using namespace std;
//良辰美景奈何天,赏心乐事谁家院
const int maxn=200000+5;
int n,s[maxn],gs[maxn],wv[maxn],wb[maxn],sa[maxn],rank[maxn],height[maxn];
inline int read(void){
char ch=getchar();int x=0;
while(!(ch>=‘0‘&&ch<=‘9‘))
ch=getchar();
while(ch>=‘0‘&&ch<=‘9‘)
x=x*10+ch-‘0‘,ch=getchar();
return x;
}
inline bool cmp(int *x,int a,int b,int l){
return x[a]==x[b]&&x[a+l]==x[b+l];
}
inline void da(int *sa,int *x,int n,int m){
int i,j,p,*y=wb;
for(i=0;i<m;i++) gs[i]=0;
for(i=0;i<n;i++) gs[x[i]]++;
for(i=1;i<m;i++) gs[i]+=gs[i-1];
for(i=n-1;~i;i--) sa[--gs[x[i]]]=i;
for(j=1,p=1;p<n;j<<=1,m=p){
for(i=n-j,p=0;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<n;i++) wv[i]=x[y[i]];
for(i=0;i<m;i++) gs[i]=0;
for(i=0;i<n;i++) gs[wv[i]]++;
for(i=1;i<m;i++) gs[i]+=gs[i-1];
for(i=n-1;~i;i--) sa[--gs[wv[i]]]=y[i];
p=1;swap(x,y);x[sa[0]]=0;
for(i=1;i<n;i++) x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
}
}
inline void calheight(int n){
int i,j,k=0;
for(i=0;i<=n;i++) rank[sa[i]]=i;
for(i=0;i<n;height[rank[i++]]=k)
for(k?k--:233,j=sa[rank[i]-1];s[i+k]==s[j+k];k++);
}
inline bool check(int x){
int Max=0,Min=n;bool ans=0;
for(int i=2;i<=n&&ans==0;i++){
if(height[i]<x){
if(Max!=0)
if(Max-Min>x)
ans=1;
Min=Max=sa[i];
continue;
}
Max=max(Max,sa[i]),Min=min(Min,sa[i]);
}
if(Max!=0&&Max-Min>x)
ans=1;
return ans;
}
signed main(void){
while(n=read()){
memset(s,0,sizeof(s));
memset(gs,0,sizeof(gs));
memset(wb,0,sizeof(wb));
memset(wv,0,sizeof(wv));
memset(sa,0,sizeof(sa));
memset(rank,0,sizeof(rank));
memset(height,0,sizeof(height));
for(int i=0;i<n;i++)
s[i]=read();
if(n<10){
puts("0");continue;
}
n--;
for(int i=0;i<n;i++)
rank[i]=s[i]=s[i+1]-s[i]+100;
s[n]=0;
da(sa,rank,n+1,200);calheight(n);
int l=4,r=n>>1,ans=-1;
while(l<=r){
int mid=(l+r)>>1;
if(check(mid))
ans=mid,l=mid+1;
else
r=mid-1;
}
printf("%d\n",ans+1);
}
return 0;
}//Cap ou pas cap. Pas cap.
未完待续...
By NeighThorn
原文:http://www.cnblogs.com/neighthorn/p/6291738.html