NKOJ 4151 (TJOI 2016&HEOI 2016)字符串(后缀数组+倍增+主席树)

来源:互联网 发布:淘宝的保证金怎么退 编辑:程序博客网 时间:2024/06/08 16:30

【Tjoi2016&Heoi2016】字符串

问题描述

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

输入格式

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

输出格式

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

样例输入

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
2
2
2

提示

1<=n,m<=100,000,字符串中仅有小写英文字母,a<=b,c<=d,1<=a,b,c,d<=n


此题思路比较直接,考虑到直接搞的麻烦,考虑二分答案,那么问题可以转化成求[a,bmid+1]中是否存在一个位置开始的后缀与以c为开头的后缀的最长公共前缀大于mid

考虑如何判断,考虑SA数组,显然与c开头的后缀最长公共前缀大于mid的后缀在SA数组中是连续的一个区间,那么问题即是求是否存在以[a,bmid+1]开头的后缀在SA数组中的位置恰好在上述区间中,那么用主席树维护每个位置对应SA数组中出现位置的情况,直接查找即可。

关于找出区间,RMQ处理Height数组,然后倍增查找即可。


代码:

#include<stdio.h>#include<iostream>#include<algorithm>#include<cstring>#define N 200005#define M 10000005using namespace std;char s[N];int n,m,SA[N],Rank[N],H[N],F[N][20],S=18;int wa[N],wb[N],T[N];int tot,ls[M],rs[M],v[M],rt[N];bool equ(int *r,int a,int b,int l){return r[a]==r[b]&&r[a+l]==r[b+l];}void GSA(char *r,int *sa,int a,int b){    int i,j,p,*x=wa,*y=wb,*t;    for(i=0;i<b;i++)T[i]=0;    for(i=0;i<a;i++)T[x[i]=r[i]]++;    for(i=1;i<b;i++)T[i]+=T[i-1];    for(i=a-1;i>=0;i--)sa[--T[x[i]]]=i;    for(p=1,j=1;p<a;j<<=1,b=p)    {        for(p=0,i=a-j;i<a;i++)y[p++]=i;        for(i=0;i<a;i++)if(sa[i]>=j)y[p++]=sa[i]-j;        for(i=0;i<b;i++)T[i]=0;        for(i=0;i<a;i++)T[x[y[i]]]++;        for(i=1;i<b;i++)T[i]+=T[i-1];        for(i=a-1;i>=0;i--)sa[--T[x[y[i]]]]=y[i];        for(t=x,x=y,y=t,p=1,x[sa[0]]=0,i=1;i<a;i++)        x[sa[i]]=equ(y,sa[i-1],sa[i],j)?p-1:p++;    }}void GH(char *r,int *sa,int a){    int i,j,k=0;    for(i=1;i<=a;i++)Rank[sa[i]]=i;    for(i=0;i<a;H[Rank[i++]]=k)    for(k?k--:0,j=sa[Rank[i]-1];r[i+k]==r[j+k];k++);}void RMQ(){    int i,j;    for(i=1;i<=n;i++)F[i][0]=H[i];    for(i=n;i>=1;i--)    for(j=1;i+(1<<j-1)<=n;j++)    F[i][j]=min(F[i][j-1],F[i+(1<<j-1)][j-1]);}int CP(int x){    int p=++tot;    ls[p]=ls[x];    rs[p]=rs[x];    v[p]=v[x];    return p;}int ADD(int p,int l,int r,int k){    int o=CP(p);    if(l==r){v[o]++;return o;}    int mid=l+r>>1;    if(k<=mid)ls[o]=ADD(ls[o],l,mid,k);    else rs[o]=ADD(rs[o],mid+1,r,k);    v[o]=v[ls[o]]+v[rs[o]];    return o;}int GS(int lp,int rp,int l,int r,int x,int y){    if(x<=l&&y>=r)return v[rp]-v[lp];    int mid=l+r>>1,cs=0;    if(x<=mid&&y>=l)cs+=GS(ls[lp],ls[rp],l,mid,x,y);    if(x<=r&&y>mid)cs+=GS(rs[lp],rs[rp],mid+1,r,x,y);    return cs;}bool ok(int t,int a,int b,int c){    int l=Rank[c],r=Rank[c];    for(int i=S;i>=0;i--)    {        if(l-(1<<i)>=0&&F[l-(1<<i)+1][i]>=t)l=l-(1<<i);        if(r+(1<<i)<=n&&F[r+1][i]>=t)r=r+(1<<i);    }    return GS(rt[a-1],rt[b],1,n,l,r);}void EF(int l,int r,int a,int b,int c){    int mid;    while(l<=r)    {        mid=l+r>>1;        if(ok(mid,a,b-mid+1,c))l=mid+1;        else r=mid-1;    }    printf("%d\n",r);}int main(){    int a,b,c,d;    scanf("%d%d%s",&n,&m,s);s[n]='a'-1;    GSA(s,SA,n+1,200);GH(s,SA,n);RMQ();    rt[0]=ADD(rt[0],1,n,Rank[0]);    for(int i=1;i<n;i++)rt[i]=ADD(rt[i-1],1,n,Rank[i]);    while(m--)    {        scanf("%d%d%d%d",&a,&b,&c,&d);        a--;b--;c--;d--;        EF(1,min(b-a+1,d-c+1),a,b,c);    }}
阅读全文
0 0
原创粉丝点击