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(后缀数组[不可重叠最长重复子串])

POJ-1743 Musical Theme 字符串问题 不重叠最长重复子串

POJ 1743 后缀数组

POJ - 1743 后缀数组 height分组

poj 1743 Musical Theme