[LuoguP4094] [HEOI2016] [TJOI2016]字符串(二分答案+后缀数组+ST表+主席树)

Posted birchtree

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[LuoguP4094] [HEOI2016] [TJOI2016]字符串(二分答案+后缀数组+ST表+主席树)相关的知识,希望对你有一定的参考价值。

[LuoguP4094] [HEOI2016] [TJOI2016]字符串(二分答案+后缀数组+ST表+主席树)

题面

给出一个长度为(n)的字符串(s),以及(m)组询问.每个询问是一个四元组((a,b,c,d)),问(s[a,b])的所有子串和字符串(s[c,d])的最长公共前缀长度的最大值。

(n,m leq 10^5)

分析

显然答案有单调性。首先我们二分答案(mid),考虑如何判定。

如果mid这个答案可行,那么一定存在一个后缀x,它的开头在([a,b-mid+1])中,且(lcp(x,s[c,d]) geq mid)

根据我们用height数组求LCP的方法,满足条件的后缀S一定在(rank[c])向左和向右形成的连续区间内。那么我们可以用ST表+二分查找求出这个区间([p,q])

那么问题就转化为,([a,b-mid+1])中是否存在位置的rank在([p,q])之间。因此我们可以对rank数组建出可持久化的线段树,在上面查询落在([p,q])之间的值的个数。如果个数大于0,则mid这个答案合法。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#define maxn 100000
#define maxs 128
#define maxlogn 20
#define INF 0x3f3f3f3f
using namespace std;
int n,m;
char in[maxn+5];

void rsort(int *ans,int *fi,int *se,int n,int m){
    static int buck[maxn+5];
    for(int i=0;i<=m;i++) buck[i]=0;
    for(int i=1;i<=n;i++) buck[fi[i]]++;
    for(int i=1;i<=m;i++) buck[i]+=buck[i-1];
    for(int i=n;i>=1;i--) ans[buck[fi[se[i]]]--]=se[i];
}
int sa[maxn+5],rk[maxn+5],height[maxn+5];
void suffix_sort(char *s,int n,int m){
    static int se[maxn+5];
    for(int i=1;i<=n;i++){
        rk[i]=s[i];
        se[i]=i;
    }
    rsort(sa,rk,se,n,m);
    for(int k=1;k<=n;k*=2){
        int p=0;
        for(int i=n-k+1;i<=n;i++) se[++p]=i;
        for(int i=1;i<=n;i++) if(sa[i]>k) se[++p]=sa[i]-k;
        rsort(sa,rk,se,n,m);
        swap(rk,se);
        rk[sa[1]]=1;
        p=1;
        for(int i=2;i<=n;i++){
            if(se[sa[i-1]]==se[sa[i]]&&se[sa[i-1]+k]==se[sa[i]+k]) rk[sa[i]]=p;
            else rk[sa[i]]=++p;
        }
        if(p==n) break;
        m=p;
    }
}
void get_height(char *s,int n,int m){
    suffix_sort(s,n,m);
    for(int i=1;i<=n;i++) rk[sa[i]]=i;
    int k=0;
    for(int i=1;i<=n;i++){
        if(k) k--;
        int j=sa[rk[i]-1];
        while(s[i+k]==s[j+k]) k++;
        height[rk[i]]=k;
    }
}

struct sparse_table{
    int log2[maxn+5];
    int st[maxn+5][maxlogn+5];
    void ini(int *a,int n){
        log2[0]=-1;
        for(int i=1;i<=n;i++) log2[i]=log2[i>>1]+1;
        memset(st,0x3f,sizeof(st));
        for(int i=1;i<=n;i++) st[i][0]=a[i];
        for(int j=1;(1<<j)<=n;j++){
            for(int i=1;i+(1<<(j-1))-1<=n;i++){
                st[i][j]=min(st[i][j-1],st[i+(1<<(j-1))][j-1]);
            }
        }
    }
    int query(int l,int r){
        if(l>r) return INF;
        int k=log2[r-l+1];
        return min(st[l][k],st[r-(1<<k)+1][k]);
    }
}St;
int lcp(int x,int y){
    //从x位置开始的后缀,与从y位置开始的后缀的lcp 
    if(rk[x]>rk[y]) swap(x,y);
    return St.query(rk[x]+1,rk[y]); 
}
int find_lbound(int c,int val){
    //找到height数组上从rk[c]往左值>=val的区间
    int l=1,r=rk[c];
    int mid,ans=r;
    while(l<=r){
        mid=(l+r)>>1;
        if(St.query(mid+1,rk[c])>=val){//注意这里+1,因为求lcp求的是[rk[x]+1,rk[y]] 
            ans=mid;
            r=mid-1;
        }else l=mid+1;
    } 
    return ans;
}
int find_rbound(int c,int val){
    //找到height数组上从rk[c]往右值>=val的区间
    int l=rk[c],r=n;
    int mid,ans=l;
    while(l<=r){
        mid=(l+r)>>1;
        if(St.query(rk[c]+1,mid)>=val){
            ans=mid;
            l=mid+1;
        }else r=mid-1;
    } 
    return ans;
}

struct persist_segment_tree{
#define lson(x) (tree[x].ls)
#define rson(x) (tree[x].rs)
    struct node{
        int ls;
        int rs;
        int cnt;
    }tree[maxn*maxlogn+5];
    int root[maxn+5];
    int ptr;
    inline void push_up(int x){
        tree[x].cnt=tree[lson(x)].cnt+tree[rson(x)].cnt;
    } 
    void insert(int &x,int last,int val,int l,int r){
        x=++ptr;
        tree[x]=tree[last];
        if(l==r){
            tree[x].cnt++;
            return;
        }
        int mid=(l+r)>>1;
        if(val<=mid) insert(lson(x),lson(last),val,l,mid);
        else insert(rson(x),rson(last),val,mid+1,r);
        push_up(x);
    }
    int query(int x,int L,int R,int l,int r){
        if(L<=l&&R>=r) return tree[x].cnt;
        int mid=(l+r)>>1;
        int ans=0;
        if(L<=mid) ans+=query(lson(x),L,R,l,mid);
        if(R>mid) ans+=query(rson(x),L,R,mid+1,r);
        return ans;
    }
    void ini(int *a,int n){
        for(int i=1;i<=n;i++){
            insert(root[i],root[i-1],a[i],0,n);
        }
    }
    int Query(int pl,int pr,int vl,int vr){
        if(vl==vr) return 0;
        //查询数组从[pl,pr]处于[vl,vr]中的值的数量 
//      printf("d:%d
",query(root[pr],vl,vr,0,n));
//      printf("d:%d
",query(root[pl-1],vl,vr,0,n));
        return query(root[pr],vl,vr,0,n)-query(root[pl-1],vl,vr,0,n);
    }
#undef lson
#undef rson
}Tr;

bool check(int mid,int a,int b,int c,int d){
    if(mid==0) return 1; 
    int lb=find_lbound(c,mid);
    int rb=find_rbound(c,mid);
    if(Tr.Query(a,b-mid+1,lb,rb)>0) return 1;
    else return 0; 
}
int get_ans(int a,int b,int c,int d){
    int l=0,r=min(b-a+1,d-c+1);
    int mid,ans=l;
    check(1,a,b,c,d);
    while(l<=r){
        mid=(l+r)>>1;
        if(check(mid,a,b,c,d)){
            ans=mid;
            l=mid+1;
        }else r=mid-1;
    }
    return ans;
}

int main(){
    int a,b,c,d;
    scanf("%d %d",&n,&m);
    scanf("%s",in+1);
    get_height(in,n,maxs);
    St.ini(height,n);
    Tr.ini(rk,n);
    for(int i=1;i<=m;i++){
        scanf("%d %d %d %d",&a,&b,&c,&d);
        printf("%d
",get_ans(a,b,c,d)); 
    } 
}
/*
7 3
cccbbba
3 6 3 4
*/ 

以上是关于[LuoguP4094] [HEOI2016] [TJOI2016]字符串(二分答案+后缀数组+ST表+主席树)的主要内容,如果未能解决你的问题,请参考以下文章

[题解] LuoguP4091 [HEOI2016/TJOI2016]求和

luoguP4113 [HEOI2012]采花

luoguP4098「HEOI2013」ALO

洛谷P4094 - [TJOI2016]字符串

[HEOI2016/TJOI2016]排序

P4093[HEOI2016/TJOI2016]序列