[BZOJ4552][Tjoi2016&Heoi2016]字符串-后缀数组-主席树

字符串

Description

佳媛姐姐过生日的时候,她的小伙伴从某东上买了一个生日礼物。生日礼物放在一个神奇的箱子中。箱子外边写了一个长为n的字符串s,和m个问题。佳媛姐姐必须正确回答这m个问题,才能打开箱子拿到礼物,升职加薪,出任CEO,嫁给高富帅,走上人生巅峰。每个问题均有a,b,c,d四个参数,问你子串s[a..b]的所有子串和s[c..d]的最长公共前缀的长度的最大值是多少?佳媛姐姐并不擅长做这样的问题,所以她向你求助,你该如何帮助她呢?

Input

输入的第一行有两个正整数n,m,分别表示字符串的长度和询问的个数。接下来一行是一个长为n的字符串。接下来m行,每行有4个数a,b,c,d,表示询问s[a..b]的所有子串和s[c..d]的最长公共前缀的最大值。1<=n,m<=100,000,字符串中仅有小写英文字母a<=b,c<=d,1<=a,b,c,d<=n

Output

对于每一次询问,输出答案。

Sample Input

5 5
aaaaa
1 1 1 5
1 5 1 1
2 3 2 3
2 4 2 3
2 3 2 4

Sample Output

1
1
2
2
2


想起「后缀数组」
后缀数组相关太久没写,已经几乎不记得了……


思路:
求解最长公共前缀相关问题通常会用到后缀数组。
于是从后缀数组的方向考虑。

首先,考虑一个可行的暴力:
枚举 [a,b] 中的节点 i [c,d] 中的节点 j ,求出 lcp(i,j) 并用之更新答案。

考虑如何优化这个暴力。
考虑二分答案。
对于每次二分的值 mid ,求出 height 数组中包含 sa[c] 这个位置的,最长的一段 min(h[x],h[y])mid 的区间 [x,y]
可以发现,这正是所有满足 lcp(c,i)mid 的合法的前缀的起点的集合。
也就是说,只有在 [x,y] 之中的起点才能满足与 c lcp 大于 mid

那么也就是说,若 [a,b] 中有任何一个位置在 [x,y] 之中,则 [a,b] 至少有一个子串满足与 s[c,d] lcp 答案大于等于 mid
这个位置的查询很显然可以直接上主席树~

然后就做完了,复杂度 O(nlog2n)

#include<bits/stdc++.h>
using namespace std;

inline int read()
{
    int x=0;char ch=getchar();
    while(ch<'0' || '9'<ch)ch=getchar();
    while('0'<=ch && ch<='9')x=x*10+(ch^48),ch=getchar();
    return x;
}

inline int minn(int a,int b){return a<b?a:b;}

typedef pair<int,int> pr;
const int N=100009;
const int K=23;

int n,m;
char s[N];

namespace st
{
    int h[N][K],sa[N],rk[N];
    int wc[N],wb[N],wa[N],bin[N];

    inline void build(char *s,int n,int m,int *sa)
    {
        int *x=wb,*y=wa;
        for(int i=1;i<=m;i++)
            wc[i]=0;
        for(int i=1;i<=n;i++)
            ++wc[x[i]=s[i]];
        for(int i=1;i<=m;i++)
            wc[i]+=wc[i-1];
        for(int i=n;i>=1;i--)
            sa[wc[x[i]]--]=i;
        for(int j=1,p=0;j<=n && p<n;m=p,j<<=1,p=0)
        {
            for(int i=n-j+1;i<=n;i++)
                y[++p]=i;
            for(int i=1;i<=n;i++)
                if(sa[i]>j)
                    y[++p]=sa[i]-j;

            for(int i=1;i<=m;i++)
                wc[i]=0;
            for(int i=1;i<=n;i++)
                wc[x[i]]++;
            for(int i=1;i<=m;i++)
                wc[i]+=wc[i-1];
            for(int i=n;i>=1;i--)
                sa[wc[x[y[i]]]--]=y[i];

            swap(x,y);
            x[sa[1]]=p=1;
            for(int i=2;i<=n;i++)
                if(y[sa[i-1]]==y[sa[i]] && y[sa[i-1]+j]==y[sa[i]+j])
                    x[sa[i]]=p;
                else
                    x[sa[i]]=++p;
        }

        for(int i=1;i<=n;i++)
            rk[sa[i]]=i;
        for(int i=1,k=0;i<=n;i++)
        {
            if(k)k--;
            int j=sa[rk[i]+1];
            if(rk[i]==n)continue;
            while(s[i+k]==s[j+k])
                k++;
            h[rk[i]][0]=k;
        }
    }

    inline void init()
    {
        build(s,n,26,sa);
        for(int i=2;i<=n;i++)
            bin[i]=bin[i>>1]+1;
        for(int i=1;i<=bin[n];i++)
            for(int j=1;j+(1<<i)-1<=n;j++)
            {
                if(h[j][i-1]<h[j+(1<<i-1)][i-1])
                    h[j][i]=h[j][i-1];
                else h[j][i]=h[j+(1<<i-1)][i-1];
            }
    }

    inline int query(int l,int r)
    {
        if(l>r)return 0;int dlt=bin[r-l+1];
        return minn(h[l][dlt],h[r-(1<<dlt)+1][dlt]);
    }

    inline pr eq_range(int s,int t,int len)
    {
        int pos=rk[s],l,r,mid;
        pr ret=pr(pos,pos);

        l=pos+1;r=n;
        while(l<=r)
        {
            mid=l+r>>1;
            if(query(pos,mid-1)>=len)
                l=mid+1,ret.second=mid;
            else r=mid-1;
        }

        l=1;r=pos-1;
        while(l<=r)
        {
            mid=l+r>>1;
            if(query(mid,pos-1)>=len)
                r=mid-1,ret.first=mid;
            else l=mid+1;
        }

        return ret;
    }
}

namespace pst
{
    const int M=N*40;
    int rt[N],t[M],ls[M],rs[M],tot;

    inline int modify(int pre,int l,int r,int p)
    {
        int now=++tot;t[now]=t[pre]+1;
        if(l==r)return now;int mid=l+r>>1;
        if(p<=mid)ls[now]=modify(ls[pre],l,mid,p),rs[now]=rs[pre];
        else ls[now]=ls[pre],rs[now]=modify(rs[pre],mid+1,r,p);
        return now;
    }

    inline int query(int tl,int tr,int l,int r,int dl,int dr)
    {
        if(dl==l && r==dr)return t[tr]-t[tl];
        int mid=l+r>>1;if(!(t[tr]-t[tl]))return 0;
        if(dr<=mid)return query(ls[tl],ls[tr],l,mid,dl,dr);
        if(mid<dl) return query(rs[tl],rs[tr],mid+1,r,dl,dr);
        return query(ls[tl],ls[tr],l,mid,dl,mid)+query(rs[tl],rs[tr],mid+1,r,mid+1,dr);
    }

    inline void init()
    {
        for(int i=1;i<=n;i++)
            rt[i]=modify(rt[i-1],1,n,st::sa[i]);
    }
}

inline bool check(int a,int b,int c,int d,int len)
{
    pr rg=st::eq_range(c,d,len);
    if(rg.first>rg.second)return 0;
    return pst::query(pst::rt[rg.first-1],pst::rt[rg.second],1,n,a,b)>0;
}

inline int query(int a,int b,int c,int d)
{
    int l=1,r=minn(b-a+1,d-c+1),ans=0,mid;
    while(l<=r)
    {
        mid=l+r>>1;
        if(check(a,b-mid+1,c,d,mid))
            l=mid+1,ans=mid;
        else r=mid-1;
    }
    return ans;
}

int main()
{
    n=read();m=read();
    scanf("%s",s+1);
    for(int i=1;i<=n;i++)
        s[i]=s[i]-'a'+1;
    st::init();
    pst::init();

    for(int i=1,a,b,c,d;i<=m;i++)
    {
        a=read();b=read();c=read();d=read();
        printf("%lld\n",query(a,b,c,d));
    }

    return 0;
}

猜你喜欢

转载自blog.csdn.net/zlttttt/article/details/79736181