洛谷 P4094 [HEOI2016/TJOI2016]字符串 后缀数组+二分+主席树

版权声明:2333 https://blog.csdn.net/liangzihao1/article/details/82778664

题目描述

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

输入输出格式

输入格式:
输入的第一行有两个正整数 n , m n,m ,分别表示字符串的长度和询问的个数。接下来一行是一个长为 n n 的字符串。接下来 m m 行,每行有4个数 a , b , c , d a,b,c,d ,表示询问 s [ a . . b ] s[a..b] 的所有子串和 s [ c . . d ] s[c..d] 的最长公共前缀的最大值。

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

输入输出样例

输入样例#1:
5 5
aaaaa
1 1 1 5
1 5 1 1
2 3 2 3
2 4 2 3
2 3 2 4
输出样例#1:
1
1
2
2
2
说明

对于10%的数据, 1 < = n , m < = 300 1<=n,m<=300

对于40%的数据, 1 < = n , m < = 3000 1<=n,m<=3000 ,字符串中仅有 a , b a,b

对于100%的数据, 1 < = n , m < = 100000 1<=n,m<=100000 ,字符串中仅有小写英文字母, a < = b , c < = d , 1 < = a , b , c , d < = n a<=b,c<=d,1<=a,b,c,d<=n \

分析:
S = s [ a . . b ] S=s[a..b] ,显然只有 S S 的后缀才对答案有贡献。我们先对 s s 建一个后缀数组,相当于求与以 c c 开头的后缀的与 S S 的后缀的 l c p lcp 最大值(大概可以这样理解,虽然有边界问题)。
考虑二分一个答案,显然这个答案的上限是 m i n ( b a + 1 , d c + 1 ) min(b-a+1,d-c+1) ,对于一个答案 m i d mid ,可以二分出与 c c 这个后缀的 l c p lcp 大于等于 m i d mid 的区间。显然后缀排序后,一个后缀与其他后缀的 l c p lcp 是一个单峰函数。
加入二分出来的区间为 [ l c , r c ] [lc,rc] ,而 S S 串中只有前 [ a , b m i d + 1 ] [a,b-mid+1] 个后缀长度超过 m i d mid ,相当于查询在排名为 l c lc r c rc 的后缀中,有没有一个后缀是原串的第 [ a , b m i d + 1 ] [a,b-mid+1] 位。我们可以以排名为下标建可持久化权值线段
树即可。
要先预处理 l o g log ,因为c++自带的求 l o g log 采用的是二分,复杂度是 O ( l o g n ) O(logn) 的。
所以总复杂度是 O ( n l o g 2 n ) O(nlog^2n) 级别的。

代码:

// luogu-judger-enable-o2
#include <iostream>
#include <cstdio>
#include <cmath>

const int maxn=1e5+7;

using namespace std;

int n,m,cnt;
int x[maxn],y[maxn],c[maxn],root[maxn],Log2[maxn];
char s[maxn];

struct node{
    int l,r,data;
}t[maxn*31];

struct suffix_array{
    int h[maxn][20],rank[maxn],sa[maxn];
    void getsa()
    {
        int m=1000;
        for (int i=1;i<=m;i++) c[i]=0;
        for (int i=1;i<=n;i++) x[i]=s[i];
        for (int i=1;i<=n;i++) c[x[i]]++;
        for (int i=1;i<=m;i++) c[i]+=c[i-1];
        for (int i=n;i>0;i--) sa[c[x[i]]--]=i;
        for (int k=1;k<=n;k<<=1)
        {
            int num=0;
            for (int i=n-k+1;i<=n;i++) y[++num]=i;
            for (int i=1;i<=n;i++) if (sa[i]>k) y[++num]=sa[i]-k;
            for (int i=1;i<=m;i++) c[i]=0;
            for (int i=1;i<=n;i++) c[x[i]]++;
            for (int i=1;i<=m;i++) c[i]+=c[i-1];
            for (int i=n;i>0;i--) sa[c[x[y[i]]]--]=y[i],y[i]=0;
            swap(x,y);
            num=1;
            x[sa[1]]=1;
            for (int i=2;i<=n;i++)
            {
                if ((y[sa[i]]!=y[sa[i-1]]) || (y[sa[i]+k]!=y[sa[i-1]+k]))
                {
                    x[sa[i]]=++num;
                }
                else x[sa[i]]=num;
            }
            if (num>=n) break;
            m=num;
        }
        for (int i=1;i<=n;i++) rank[i]=x[i];
    }
    void getheight()
    {
    	int k=0;
    	for (int i=1;i<=n;i++)
        {
            if (k) k--;
            int j=sa[rank[i]-1];
            while ((i+k<=n) && (j+k<=n) && (s[i+k]==s[j+k])) k++;
            h[rank[i]][0]=k;
        }
    	int c=1;
        for (int j=1;j<20;j++)
        {
            for (int i=1;i<=n;i++)
            {
            	if (i+c>n) h[i][j]=h[i][j-1];
                      else h[i][j]=min(h[i][j-1],h[i+c][j-1]);
            }
            c<<=1;
        }
    }
    int lcp(int x,int y)
    {
        x=rank[x],y=rank[y];
        if (x>y) swap(x,y);
        x++;
        int k=Log2[y-x+1];
        return min(h[x][k],h[y-(1<<k)+1][k]);
    }
}a;

void ins(int &p,int q,int l,int r,int x)
{
    if (!p) p=++cnt;
    t[p].data=t[q].data+1;
    if (l==r) return;
    int mid=(l+r)/2;
    if (x<=mid) t[p].r=t[q].r,ins(t[p].l,t[q].l,l,mid,x);
           else t[p].l=t[q].l,ins(t[p].r,t[q].r,mid+1,r,x);
}

int query(int p,int q,int l,int r,int x,int y)
{
    if ((l==x) && (r==y)) return t[p].data-t[q].data;
    if (t[p].data-t[q].data==0) return 0;
    int mid=(l+r)/2;
    if (y<=mid) return query(t[p].l,t[q].l,l,mid,x,y);
    else if (x>mid) return query(t[p].r,t[q].r,mid+1,r,x,y);
    else return query(t[p].l,t[q].l,l,mid,x,mid)+query(t[p].r,t[q].r,mid+1,r,mid+1,y);
}

int find1(int d,int x)
{
    int l=1,r=x-1,ans=x;
    while (l<=r)
    {
        int mid=(l+r)/2;
        if (a.lcp(a.sa[mid],a.sa[x])>=d) r=mid-1,ans=mid;
                                    else l=mid+1;
    }
    return ans;
}

int find2(int d,int x)
{
    int l=x+1,r=n,ans=x;
    while (l<=r)
    {
        int mid=(l+r)/2;
        if (a.lcp(a.sa[x],a.sa[mid])>=d) l=mid+1,ans=mid;
                                    else r=mid-1;
    }
    return ans;
}


int main()
{
    scanf("%d%d",&n,&m);
    scanf("%s",s+1);
    a.getsa();		
    a.getheight();			
    for (int i=1;i<=n;i++)
    {
        ins(root[i],root[i-1],1,n,a.sa[i]);
    }		
    for (int i=1;i<=n;i++) Log2[i]=trunc(log(i+0.5)/log(2));
    for (int i=1;i<=m;i++)
    {
        int x,y,A,B;
        scanf("%d%d%d%d",&x,&y,&A,&B);
        int l=1,r=min(B-A+1,y-x+1),ans=0;
        while (l<=r)
        {
            int mid=(l+r)/2;
            int lc=find1(mid,a.rank[A]),rc=find2(mid,a.rank[A]);
            if (query(root[rc],root[lc-1],1,n,x,y-mid+1)) ans=mid,l=mid+1;
                                                     else r=mid-1;
        }
        printf("%d\n",ans);
    }
}

猜你喜欢

转载自blog.csdn.net/liangzihao1/article/details/82778664