/*
Author: lcy
Time: 2017-10-6
Hihocoder1407 后缀数组 
求字符串中至少重复1次且不重叠的最长长度
与之前的题目一样,只不过这次加上了“不重叠”这个条件
最长长度满足二分答案的条件,即如果不存在长度a的连续序列满足
条件,那么长度大于a的连续序列更不可能,另一方面,可能存在长度
大于a的连续序列满足条件。
重叠部分一定出现在一段连续的LCP中,所以判断时,我们选择一段尽量长的
LCP区间,使得其中所有值都大于待检测长度,同时维护最大与最小的后缀起始点
位置,使得他们的差值大于待检测长度,这样就不会重叠。如果能找到这样一段区间,
那么意味着待检测长度可以满足条件。
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <queue>
#include <map>
#include <algorithm>
using namespace std;
#define ll long long
#define fr(i,a,b) for(int i=a;i<=b;i++)
#define frr(i,a,b) for(int i=a;i>=b;i--)
#define ms(a,b) memset(a,b,sizeof(a))
#define scfd(a) scanf("%d",a)
#define scflf(a) scanf("%lf",a)
#define scfs(a) scanf("%s",a)
#define ptfd(a) printf("%d\n",a)
#define ptfs(a) printf("%s\n",a)
#define showd(a,b) printf(a"=%d\n",b)
#define showlf(a,b) printf(a"=%lf\n",b)
#define shows(a,b) printf(a"=%s\n",b)
#define mmcp(a,b) memcpy(a,b,sizeof(b))
#define pb(a) push_back(a)

#define L 0
#define S 1
const int MAXN=100005;
inline bool same(int *s,int *tp,int l,int a,int b){
    while(a<=l&&b<=l){
        if(s[a++]!=s[b++])
            return false;
        if(tp[a]==S&&tp[a-1]==L)
            break;
        if(tp[b]==S&&tp[b-1]==L)
            break;
    }
    return s[a]==s[b];
}
//s数组字符编码从1开始,0等价于'#'
static int *sais(int *s,int l){
    //s数组第l位为0(#)
    s[l]=0;
    int SIGMA=*max_element(s,s+l+1)+13;
    int *cnt=new int[SIGMA];
    int *lp=new int[SIGMA];
    int *sp=new int[SIGMA];
    int *sa=new int[l+13];
    int *s1=new int[l+13];
    int *tp=new int[l+13];
    int *name=new int[l+13];
    int *pos=new int[l+13];
    tp[l]=S;//末尾最小
    //标记LS
    frr(i,l-1,0)
        if(s[i]>s[i+1])tp[i]=L;
        else if(s[i]<s[i+1])tp[i]=S;
        else tp[i]=tp[i+1];
    //求LSM数组
    fill(cnt,cnt+SIGMA,0);
    fr(i,0,l)cnt[s[i]]++;
    fr(i,1,SIGMA-1)cnt[i]+=cnt[i-1];//字符数量的前缀和
    lp[0]=0;
    fr(i,1,SIGMA-1)lp[i]=cnt[i-1];//求每个字符桶L型的起始位置
    fr(i,0,SIGMA-1)sp[i]=cnt[i]-1;//求每个字符桶S型的起始位置
    fill(sa,sa+l+1,-1);//注意在结尾添加了#,长度为l+1
    fr(i,1,l)if(tp[i]==S&&tp[i-1]==L)sa[sp[s[i]]--]=i;//任意顺序先放*型字符
    fr(i,0,l)if(sa[i]>0&&tp[sa[i]-1]==L)sa[lp[s[sa[i]-1]]++]=sa[i]-1;//诱导L型后缀
    fr(i,0,SIGMA-1)sp[i]=cnt[i]-1;//还原S型起始位置
    frr(i,l,0)if(sa[i]>0&&tp[sa[i]-1]==S)sa[sp[s[sa[i]-1]]--]=sa[i]-1;//诱导S型后缀
    fill(name,name+l+1,-1);
    int cur=0,last=-1,ed=0;
    bool is_same=false;
    fr(i,0,l)
        if(sa[i]>0&&tp[sa[i]]==S&&tp[sa[i]-1]==L){
            if(last!=-1){
                if(!same(s,tp,l,sa[i],last))cur++;
                else is_same=true;
            }
            name[sa[i]]=cur;
            last=sa[i];
        }
    fr(i,0,l)if(name[i]>=0){pos[ed]=i,s1[ed]=name[i];ed++;}//pos[i]表示s1[i]在s中对应的下标
    lp[0]=0;
    fr(i,1,SIGMA-1)lp[i]=cnt[i-1];//求每个字符桶L型的起始位置
    fr(i,0,SIGMA-1)sp[i]=cnt[i]-1;//求每个字符桶S型的起始位置
    fill(sa,sa+l+1,-1);//注意在结尾添加了#,长度为l+1
    //求s1的后缀数组sa1
    int *sa1;
    if(is_same)//如果s1字符串中有相同元素,那么需要递归求sa1
        sa1=sais(s1,ed-1);//s1[ed]一定为0(#排最小),所以s1递归下去长度只有ed-1(最后1位不算)
    else{//如果s1字符串中每个元素不同,直接桶排序生成sa1即可
        sa1=new int[ed+3];
        fill(sa1,sa1+ed+3,-1);
        fr(i,0,ed-1)sa1[s1[i]]=i;
    }
    //sa1诱导sa
    frr(i,ed-1,0)sa[sp[s[pos[sa1[i]]]]--]=pos[sa1[i]];//逆向添加*型,因为桶中的S的下标是倒着移动的
    fr(i,0,l)if(sa[i]>0&&tp[sa[i]-1]==L)sa[lp[s[sa[i]-1]]++]=sa[i]-1;//诱导L型后缀
    fr(i,0,SIGMA-1)sp[i]=cnt[i]-1;//还原S型起始位置
    frr(i,l,0)if(sa[i]>0&&tp[sa[i]-1]==S)sa[sp[s[sa[i]-1]]--]=sa[i]-1;//诱导S型后缀
    delete[] cnt;
    delete[] lp;
    delete[] sp;
    delete[] s1;
    delete[] sa1;
    delete[] pos;
    delete[] name;
    delete[] tp;
    return sa;
}
void getheight(int *s,int *sa,int *rk,int *hei,int l){
    //hei[i]:suf[sa[i]...l]与suf[sa[i-1]...l]的最长前缀,即排名第i的与排名第i+1的最长前缀
    //h[i] = hei[rank[i]] 即suf[i...l]与suf[p...l]的最长前缀,其中suf[p...l]排在suf[i...1]之前1位
    //有 h[i] >= h[i-1] - 1
    fr(i,0,l)rk[sa[i]]=i;
    int k=0;
    fr(i,0,l){
        if(k)k--;
        while(s[i+k]==s[sa[rk[i]-1]+k])k++;//sa[rk[i]-1]代表后缀数组中排在suf[i...l]前1位的后缀起始点
        hei[rk[i]]=k;
    }
}
int n;
int s[MAXN],rk[MAXN],hei[MAXN];
bool ok(int x,int *sa){
    int now=1,st=-1,mx=-1,mn=MAXN+100;
    while(now<=n){
        if(hei[now]>=x){
            if(st==-1)st=now;
            mx=max(mx,sa[now]);
            mx=max(mx,sa[now-1]);
            mn=min(mn,sa[now-1]);
            mn=min(mn,sa[now]);
        }
        else{
            if(st>=0){
                if(mx-mn>=x)return true;
                mx=-1,mn=MAXN+100,st=-1;
            }
        }
        now++;
    }
    return false;
}
int main(){
    scfd(&n);
    fr(i,0,n-1)scfd(s+i);
    int l=0,r=n+1;
    int *sa=sais(s,n);
    getheight(s,sa,rk,hei,n);
    while(l<r-1){
        int mid=(l+r)/2;
        if(ok(mid,sa))
            l=mid;
        else 
            r=mid;
    }
    printf("%d\n",l);
    return 0;
}

 posted on 2017-10-06 10:45  cylcy  阅读(138)  评论(0)    收藏  举报