/*
Author: lcy
Time: 2017-10-5
Hihocoder1403 后缀数组 
求字符串中至少重复k次最长的长度

既然重复,那么假如把后缀排好序,重复的片段一定在一个区间内
一段连续LCP区间内的最小值即为该区间内最长的公共前缀
现在已知了LCP,问题转化为求长度至少为k的连续区间内LCP的最小值的最大值
因为增加区间长度,并不会使该区间的最小值更大,即不会使得结果更优,所以只要考虑区间长度为k即可
用单调队列维护

*/

#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=20005;
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;
    }
}
struct mono_queue{
    int q[MAXN],st,ed;
    mono_queue(){
        st=ed=0;
        ms(q,0);
    }
    void push(int x){
        while(ed>st&&q[ed-1]>x)ed--;
        q[ed++]=x;
    }
    void pop(int x){
        if(x==q[st])st++;
    } 
    int get(){
        return q[st];
    }
}mq;
int n,k;
int s[MAXN];
int rk[MAXN],hei[MAXN];
int main(){
    scanf("%d%d",&n,&k);    
    fr(i,0,n-1)scfd(s+i);
    int *sa=sais(s,n);
    getheight(s,sa,rk,hei,n);
    fr(i,0,k-2)mq.push(hei[i]);
    int ans=mq.get();
    fr(i,k-1,n){
        mq.pop(hei[i-k+1]);
        mq.push(hei[i]);
        ans=max(ans,mq.get());
    }
    printf("%d\n",ans);
    return 0;
}

 posted on 2017-10-05 19:18  cylcy  阅读(104)  评论(0编辑  收藏  举报