Luogu

LOJ

分析

考虑二分答案 $mid$,则我们需要判定是否存在一个 $p\in[a,b-mid+1]$,满足 $\operatorname{lcp}(suf(p),suf(c))\geq mid$。

既然有 LCP,可以考虑后缀数组,则 $\operatorname{lcp}(suf(p),suf(c))\geq mid$ 相当于 $\min_{i=rk_p+1}^{rk_c}height_i$(或者 $\min_{i=rk_c+1}^{rk_p}height_i$,取决于 $rk_c$ 和 $rk_p$ 的大小关系)$\geq mid$。

而 $rk_c$ 是确定的,不难发现满足条件的 $rk_p$ 是一段区间,可以通过二分求出。假设这个范围是 $[x,y]$。

现在的问题变为判断是否存在一个 $p\in [a,b-mid+1]$ 满足 $rk_p\in[x,y]$。可以以 $rk_i$ 为下标建一棵主席树,则我们只需要询问 $[a,b-mid+1]$ 版本中 $[x,y]$ 的和并判断是否为 $0$ 即可。

然而二分+主席树并没有直接暴力找满足条件的区间跑得快

代码

// ====================================
//   author: M_sea
//   website: https://m-sea-blog.com/
// ====================================
#include <bits/stdc++.h>
#define file(x) freopen(#x".in","r",stdin); freopen(#x".out","w",stdout)
using namespace std;
typedef long long ll;

int read() {
    int X=0,w=1; char c=getchar();
    while (c<'0'||c>'9') { if (c=='-') w=-1; c=getchar(); }
    while (c>='0'&&c<='9') X=X*10+c-'0',c=getchar();
    return X*w;
}

const int N=100000+10;

int n,Q; char s[N];

namespace SA {
    int sa[N],rk[N],height[N];
    int x[N],y[N],z[N];
    int lg[N],f[17][N];
    void SuffixSort() {
        int m=122;
        for (int i=1;i<=n;++i) ++z[x[i]=s[i]];
        for (int i=2;i<=m;++i) z[i]+=z[i-1];
        for (int i=n;i;--i) sa[z[x[i]]--]=i;
        for (int k=1;k<=n;k<<=1) {
            int p=0;
            for (int i=n-k+1;i<=n;++i) y[++p]=i;
            for (int i=1;i<=n;++i) if (sa[i]>k) y[++p]=sa[i]-k;
            for (int i=1;i<=m;++i) z[i]=0;
            for (int i=1;i<=n;++i) ++z[x[i]];
            for (int i=2;i<=m;++i) z[i]+=z[i-1];
            for (int i=n;i;--i) sa[z[x[y[i]]]--]=y[i],y[i]=0;
            swap(x,y),x[sa[1]]=p=1;
            for (int i=2;i<=n;++i)
                x[sa[i]]=(y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+k]==y[sa[i]+k])?p:++p;
            if (p==n) break; m=p;
        }
    }
    void GetHeight() {
        for (int i=1;i<=n;++i) rk[sa[i]]=i;
        for (int i=1,j=0;i<=n;++i) {
            if (j) --j; int p=sa[rk[i]-1];
            for (;s[i+j]==s[p+j];++j);
            height[rk[i]]=j;
        }
    }
    void buildST() {
        for (int i=2;i<=n;++i) lg[i]=lg[i>>1]+1;
        for (int i=1;i<=n;++i) f[0][i]=height[i];
        for (int i=1;i<17;++i)
            for (int j=1;j<=n;++j)
                f[i][j]=min(f[i-1][j],f[i-1][j+(1<<(i-1))]);
    }
    int LCP_(int l,int r) {
        int t=lg[r-l+1];
        return min(f[t][l],f[t][r-(1<<t)+1]);
    }
    int LCP(int x,int y) {
        if (rk[x]>rk[y]) swap(x,y);
        return LCP_(rk[x]+1,rk[y]);
    }
    void build() { SuffixSort(); GetHeight(); buildST(); }
}

#define ls(o) ch[o][0]
#define rs(o) ch[o][1]
int rt[N],tot=0;
int ch[N*30][2],sz[N*30];
void modify(int& o,int f,int l,int r,int p) {
    o=++tot,ls(o)=ls(f),rs(o)=rs(f),sz[o]=sz[f]+1;
    if (l==r) return;
    int mid=(l+r)>>1;
    if (p<=mid) modify(ls(o),ls(f),l,mid,p);
    else modify(rs(o),rs(f),mid+1,r,p);
}
int query(int o,int l,int r,int ql,int qr) {
    if (!o) return 0;
    if (ql<=l&&r<=qr) return sz[o];
    int mid=(l+r)>>1,res=0;
    if (ql<=mid) res+=query(ls(o),l,mid,ql,qr);
    if (qr>mid) res+=query(rs(o),mid+1,r,ql,qr);
    return res;
}
#undef ls
#undef rs

int findL(int p,int l) {
    int L=1,R=p;
    while (L<R) {
        int mid=(L+R)>>1;
        if (SA::LCP_(mid+1,p)>=l) R=mid;
        else L=mid+1;
    }
    return L;
}
int findR(int p,int l) {
    int L=p,R=n;
    while (L<R) {
        int mid=(L+R+1)>>1;
        if (SA::LCP_(p+1,mid)>=l) L=mid;
        else R=mid-1;
    }
    return L;
}

int main() {
    n=read(),Q=read(); scanf("%s",s+1);
    SA::build();
    for (int i=1;i<=n;++i)
        modify(rt[i],rt[i-1],1,n,SA::rk[i]);
    while (Q--) {
        int a=read(),b=read(),c=read(),d=read();
        int L=0,R=min(b-a+1,d-c+1);
        while (L<R) {
            int mid=(L+R+1)>>1;
            int x=findL(SA::rk[c],mid),y=findR(SA::rk[c],mid);
            if (query(rt[b-mid+1],1,n,x,y)-query(rt[a-1],1,n,x,y)) L=mid;
            else R=mid-1;
        }
        printf("%d\n",L);
    }
    return 0;
}
最后修改:2020 年 06 月 09 日 03 : 26 PM