[TJOI&HEOI2016]str/[JZOJ4614]字符串

题目大意

给出只包含小写拉丁字母的字符串 S ,有m个询问,给出 (a,b,c,d) ,求属于区间 [a,b] 的所有子串与 S[c..d] LCP 长度最大值。

1|S|,m105


题目分析

显然可以使用后缀数组,我们要求的是

maxi[a,b]{min(dc+1,bi+1,LCP(sufi,sufc))}

根据后缀数组的性质, LCP 可以在排序后的数组上使用 RMQ 解决。
如果没有 bi+1 的限制,我们显然选择 rank rankc 尽量近的作为开头可以使 LCP 最大,对原串位置开一棵主席树,在位置 x 作为根的子树中插入rankx,区间查询离 rankc 最近位置即可。
但是我们需要考虑 bi+1 的限制,它使得 LCP 大也不一定更优。
因此我们需要设法排除对 bi+1 min 的影响。考虑二分答案。我们期望答案为 mid ,那么作为开头 [bmid+1,b] 显然都不能贡献那么多的答案,只有区间 [a,bmid] 可能存在长度大于等于 mid LCP 。那么我们就可以在这段区间查询离 rankc 最近的位置,得到最大的 LCP ,与 mid 比较一下即可。
时间复杂度 O(nlog22n)
最后%wyx大神使用后缀自动机怒切此题。


代码实现

#include <iostream>
#include <cstdio>
#include <cctype>
#include <cmath>

using namespace std;

int read()
{
    int x=0,f=1;
    char ch=getchar();
    while (!isdigit(ch)) f=ch=='-'?-1:f,ch=getchar();
    while (isdigit(ch)) x=x*10+ch-'0',ch=getchar();
    return x*f;
}

const int N=105000;
const int LGN=17;
const int S=N*LGN;

struct chairman_tree
{
    int son[S][2],size[S];
    int tot;

    int newnode(int cp)
    {
        size[++tot]=size[cp],son[tot][0]=son[cp][0],son[tot][1]=son[cp][1];
        return tot;
    }

    void add(int &rt,int rt0,int x,int l,int r)
    {
        rt=newnode(rt0);
        size[rt]++;
        if (l==r) return;
        int mid=l+r>>1;
        if (x<=mid) add(son[rt][0],son[rt0][0],x,l,mid);
        else add(son[rt][1],son[rt0][1],x,mid+1,r);
    }

    int gol(int rt,int rt0,int l,int r)
    {
        if (!(size[rt]-size[rt0])) return -1;
        if (l==r) return l;
        int mid=l+r>>1,ret;
        ret=gol(son[rt][0],son[rt0][0],l,mid);
        if (ret!=-1) return ret;
        return gol(son[rt][1],son[rt0][1],mid+1,r);
    }

    int searchl(int rt,int rt0,int st,int en,int l,int r)
    {
        if (st>en) return -1;
        if (!(size[rt]-size[rt0])) return -1;
        if (st==l&&en==r) return gol(rt,rt0,l,r);
        int mid=l+r>>1,ret;
        if (en<=mid) return searchl(son[rt][0],son[rt0][0],st,en,l,mid);
        else if (mid+1<=st) return searchl(son[rt][1],son[rt0][1],st,en,mid+1,r);
        else
        {
            ret=searchl(son[rt][0],son[rt0][0],st,mid,l,mid);
            if (ret!=-1) return ret;
            return searchl(son[rt][1],son[rt0][1],mid+1,en,mid+1,r);
        }
    }

    int gor(int rt,int rt0,int l,int r)
    {
        if (!(size[rt]-size[rt0])) return -1;
        if (l==r) return l;
        int mid=l+r>>1,ret;
        ret=gor(son[rt][1],son[rt0][1],mid+1,r);
        if (ret!=-1) return ret;
        return gor(son[rt][0],son[rt0][0],l,mid);
    }

    int searchr(int rt,int rt0,int st,int en,int l,int r)
    {
        if (st>en) return -1;
        if (!(size[rt]-size[rt0])) return -1;
        if (st==l&&en==r) return gor(rt,rt0,l,r);
        int mid=l+r>>1,ret;
        if (en<=mid) return searchr(son[rt][0],son[rt0][0],st,en,l,mid);
        else if (mid+1<=st) return searchr(son[rt][1],son[rt0][1],st,en,mid+1,r);
        else
        {
            ret=searchr(son[rt][1],son[rt0][1],mid+1,en,mid+1,r);
            if (ret!=-1) return ret;
            return searchr(son[rt][0],son[rt0][0],st,mid,l,mid);
        }
    }
}t;

int Ws[N],Wv[N],x[N],y[N],rank[N],SA[N],height[N],root[N];
int rmq[N][LGN];
int n,m,lgn,ans;
char s[N];

bool cmp(int *r,int st1,int st2,int l){return st1+l<n&&st2+l<n&&r[st1]==r[st2]&&r[st1+l]==r[st2+l];}

void DA()
{
    int i,l=1,p,mx;
    for (mx=0,i=0;i<n;i++) mx=max(mx,Wv[i]=x[i]=s[i]-'a');
    for (i=0;i<=mx;i++) Ws[i]=0;
    for (i=0;i<n;i++) Ws[Wv[i]]++;
    for (i=1;i<=mx;i++) Ws[i]+=Ws[i-1];
    for (i=n-1;i>=0;i--) SA[--Ws[Wv[i]]]=i;
    for (x[SA[0]]=p=0,i=1;i<n;i++) x[SA[i]]=(p+=s[SA[i]]!=s[SA[i-1]]);
    for (;l<=n&&p!=n-1;l<<=1)
    {
        for (p=0,i=n-l;i<n;i++) y[p++]=i;
        for (i=0;i<n;i++) if (SA[i]>=l) y[p++]=SA[i]-l;
        for (mx=0,i=0;i<n;i++) mx=max(mx,Wv[i]=x[y[i]]);
        for (i=0;i<=mx;i++) Ws[i]=0;
        for (i=0;i<n;i++) Ws[Wv[i]]++;
        for (i=1;i<=mx;i++) Ws[i]+=Ws[i-1];
        for (i=n-1;i>=0;i--) SA[--Ws[Wv[i]]]=y[i];
        for (i=0;i<n;i++) y[i]=x[i],x[i]=0;
        for (x[SA[0]]=p=0,i=1;i<n;i++) x[SA[i]]=(p+=!cmp(y,SA[i-1],SA[i],l));
    }
    for (i=0;i<n;i++) rank[SA[i]]=i;
}

void getheight()
{
    for (int i=0,j,k=0;i<n;i++,k?k--:k)
    {
        if (!rank[i]) continue;
        j=SA[rank[i]-1];
        for (;i+k<n&&j+k<n&&s[i+k]==s[j+k];) k++;
        height[rank[i]]=k;
    }
}

void pre()
{
    lgn=trunc(log(n)/log(2));
    for (int i=0;i<n;i++) rmq[i][0]=height[i];
    for (int j=1;j<=lgn;j++)
        for (int i=0;i+(1<<j)-1<n;i++)
            if (rmq[i][j-1]<rmq[i+(1<<j-1)][j-1]) rmq[i][j]=rmq[i][j-1];
            else rmq[i][j]=rmq[i+(1<<j-1)][j-1];
    for (int i=0;i<n;i++)
        t.add(root[i],i?root[i-1]:0,rank[i],0,n-1);
}

int getrmq(int l,int r)
{
    int lgr=trunc(log(r-l+1)/log(2));
    return rmq[l][lgr]<rmq[r-(1<<lgr)+1][lgr]?rmq[l][lgr]:rmq[r-(1<<lgr)+1][lgr];
}

int get(int st,int en,int x)
{
    int y=t.searchr(root[en],st?root[st-1]:0,0,rank[x],0,n-1),ret=0;
    if (y!=-1) ret=y!=rank[x]?getrmq(y+1,rank[x]):n-SA[y];
    y=t.searchl(root[en],st?root[st-1]:0,rank[x],n-1,0,n-1);
    if (y!=-1) ret=max(ret,y!=rank[x]?getrmq(rank[x]+1,y):n-SA[y]);
    return ret;
}

int LCP(int st,int en,int x)
{
    int l=st,r=en,mid,tmp,ret=0;
    while (l<=r)
    {
        mid=l+r>>1;
        tmp=get(st,mid,x);
        if (tmp>=en-mid+1) ret=en-mid+1,r=mid-1;
        else l=mid+1;
    }
    return ret;
}

void solve()
{
    for (int i=1,a,b,c,d;i<=m;i++)
    {
        a=read()-1,b=read()-1,c=read()-1,d=read()-1;
        ans=LCP(a,b,c);
        ans=min(ans,d-c+1);
        printf("%d\n",ans);
    }
}

int main()
{
    freopen("string.in","r",stdin),freopen("string.out","w",stdout);
    n=read(),m=read();
    scanf("%s",s);
    DA(),getheight(),pre();
    solve();
    fclose(stdin),fclose(stdout);
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值