poj1743(不可重叠最长重复子串,二分+后缀数组)
Posted 吃花椒的妙酱
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了poj1743(不可重叠最长重复子串,二分+后缀数组)相关的知识,希望对你有一定的参考价值。
传送门
题目大意:不可重叠最长重复子串数
思路:二分+后缀数组
与poj3261很像,不过是不可重叠
显然答案具有单调性,假设最长长度为x
我们对后缀们进行分组,height >= x且rank连续的后缀分在一组,rank连续是因为只有连续才能使后缀的重复率最高。height>=x是因为要满足长度大于x。
转化一下题意,二分x,是否存在两个长度为x的子串不重叠,我们利用sa数组来判重叠,两个子串abs(sa[i] - sa[j]) > x即存在,由于同一组里可能有多个满足条件的且可能跨越式的满足,对于同一组里的后缀我们维护最大最小的sa,max- min > x即存在。
注意max,min 的初始化,max = min = sa[1]
对了,这题一开始要将输入数据转化成差分数组,当子串长度小于4时输出即0,满足输出长度+1(差分还原)。
#include<iostream>
#include<cstring>
using namespace std;
#define _for(i,a,b) for(int i=(a) ;i<=(b) ;i++)
#define _rep(i,a,b) for(int i=(a) ;i>=(b) ;i--)
#define IOS ios::sync_with_stdio(false)
typedef long long ll;
const int N=20010;
int wa[N],wb[N],wv[N],wss[N],rak[N],height[N],cal[N],n,sa[N];
char s[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++) wss[i]=0;
for(i=0;i<n;i++) wss[x[i]=r[i]]++;
for(i=1;i<M;i++) wss[i]+=wss[i-1];
for(i=n-1;i>=0;i--) sa[--wss[x[i]]]=i;
for(j=1,p=1;p<n;j*=2,M=p) {
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;
for(i=0;i<n;i++) wv[i]=x[y[i]];
for(i=0;i<M;i++) wss[i]=0;
for(i=0;i<n;i++) wss[wv[i]]++;
for(i=1;i<M;i++) wss[i]+=wss[i-1];
for(i=n-1;i>=0;i--) sa[--wss[wv[i]]]=y[i];
for(t=x,x=y,y=t,p=1,x[sa[0]]=0,i=1;i<n;i++)
x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
}
return;
}
void calheight(int *r,int *sa,int n) {
int i,j,k=0;
for(i=1;i<=n;i++) rak[sa[i]]=i;
for(i=0;i<n;height[rak[i++]]=k)
for(k?k--:0,j=sa[rak[i]-1];r[i+k]==r[j+k];k++);
for(int i=n;i;i--)rak[i]=rak[i-1],sa[i]++;
}
bool check(int mid)
{
int cnt=0;
int l=sa[1],r=sa[1];//注意最小和最大值初始化
for(int i=1 ;i<=n ;i++)
{
if( height[i] >= mid )//如何当前还在同一个height组,维护sa最大最小值
{
l = min(l,sa[i]);
r = max(r,sa[i]);
}
else
{
l = r = sa[i];
}
if( r-l > mid ) return true;
}
return false;
}
int a[N];
int main(){
IOS;
while( cin>>n )
{
if( !n ) break;
_for(i,1,n) cin>>a[i];
_for(i,1,n-1) cal[i] = a[i+1] - a[i] + 88 ;//差分数组
n--;
cal[n+1]=0;
da(cal+1,sa,n+1,200);
calheight(cal+1,sa,n);
int l=1,r=n;;
while( l<=r )
{
int mid = (l+r)>>1;
if( check(mid)) l = mid+1;
else r = mid-1;
}
if( r < 4 ) cout<<"0"<<endl;
else cout<<r+1<<endl;
}
}
以上是关于poj1743(不可重叠最长重复子串,二分+后缀数组)的主要内容,如果未能解决你的问题,请参考以下文章
POJ 1743 Musical Theme(后缀数组[不可重叠最长重复子串])
POJ 1743 Musical Theme(后缀数组[不可重叠最长重复子串])