bzoj 4556 [Tjoi2016&Heoi2016]字符串

来源:互联网 发布:37龙神契约进阶数据库 编辑:程序博客网 时间:2024/06/16 19:23

4556: [Tjoi2016&Heoi2016]字符串

Time Limit: 20 Sec  Memory Limit: 128 MB
Submit: 986  Solved: 386
[Submit][Status][Discuss]

Description

佳媛姐姐过生日的时候,她的小伙伴从某东上买了一个生日礼物。生日礼物放在一个神奇的箱子中。箱子外边写了
一个长为n的字符串s,和m个问题。佳媛姐姐必须正确回答这m个问题,才能打开箱子拿到礼物,升职加薪,出任CE
O,嫁给高富帅,走上人生巅峰。每个问题均有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

HINT

Source






【分析】

可持久化线段树+后缀数组+二分答案

YY一下发现,[a,b]区间中与rank[c]最近的rank[x],也就是rank数组中rank[c]在数值上的前驱后继,答案最优。

二分一发答案,可持久化线段树找一下前驱后继这题就做完了。


WA了好几次,结果是因为后缀数组求错了。

唉,求尼玛老公抱



【代码】

//bzoj 4556 字符串#include<bits/stdc++.h>#define ll long long#define M(a) memset(a,0,sizeof a)#define fo(i,j,k) for(i=j;i<=k;i++)using namespace std;const int mxn=400005;const int M=4000005;char s[mxn];int n,m,T,tot,A,B,C,D,Q,len;int ls[M],rs[M],c[M],root[mxn];int a[mxn],b[mxn],x[mxn],y[mxn];int sa[mxn],rank[mxn],height[mxn],st[mxn][30];inline void build(int l,int r,int u,int &v,int w){    v=(++tot),c[v]=c[u]+1;    if(l==r) return;    int mid=l+r>>1;    ls[v]=ls[u],rs[v]=rs[u];    if(w<=mid) build(l,mid,ls[u],ls[v],w);    else build(mid+1,r,rs[u],rs[v],w);}inline int pre(int l,int r,int u,int v,int w){    int mid=l+r>>1;    if(c[v]==c[u]) return 0;    if(l==r) return l;    if(w<=mid) return pre(l,mid,ls[u],ls[v],w);    else    {        int tmp=pre(mid+1,r,rs[u],rs[v],w);        if(tmp) return tmp;        return pre(l,mid,ls[u],ls[v],w);    }}inline int nxt(int l,int r,int u,int v,int w){    int mid=l+r>>1;    if(c[v]==c[u]) return 0;    if(l==r) return l;    int Le=c[ls[v]]-c[ls[u]],Re=c[rs[v]]-c[rs[u]];    if(w>mid) return nxt(mid+1,r,rs[u],rs[v],w);    else    {        int tmp=nxt(l,mid,ls[u],ls[v],w);        if(tmp) return tmp;        return nxt(mid+1,r,rs[u],rs[v],w);    }}inline bool comp(int i,int j,int l){    return y[i]==y[j] && (i+l>len?-1:y[i+l])==(j+l>len?-1:y[j+l]);}inline void work(){    int i,j,k,p;m=30;    scanf("%d%d",&len,&Q);    scanf("%s",s+1);    fo(i,1,len) a[i]=s[i]-'a'+1;    fo(i,0,m) b[i]=0;    fo(i,1,len) b[x[i]=a[i]]++;    fo(i,1,m) b[i]+=b[i-1];    for(i=len;i>=1;i--) sa[b[x[i]]--]=i;    for(k=1;k<=len;k<<=1)    {        p=0;        fo(i,len-k+1,len) y[++p]=i;        fo(i,1,len) if(sa[i]>k) y[++p]=sa[i]-k;        fo(i,0,m) b[i]=0;        fo(i,1,len) b[x[y[i]]]++;        fo(i,1,m) b[i]+=b[i-1];        for(i=len;i>=1;i--) sa[b[x[y[i]]]--]=y[i];        swap(x,y),p=2,x[sa[1]]=1;        fo(i,2,len)          x[sa[i]]=comp(sa[i-1],sa[i],k)?p-1:p++;        if(p>len) break;        m=p;    }    p=k=0;    fo(i,1,len) rank[sa[i]]=i;    for(i=1;i<=len;height[rank[i++]]=k)      for(k?k--:0,j=sa[rank[i]-1];a[i+k]==a[j+k];k++);    fo(i,1,len) st[i][0]=height[i];    fo(j,1,18) fo(i,1,len) if(i+(1<<j-1)<=len)      st[i][j]=min(st[i][j-1],st[i+(1<<j-1)][j-1]);}inline int mn(int x,int y){    if(x==rank[y]) return len-y+1;    y=rank[y];    if(x>y) swap(x,y);    x++;int k=0;    while(x+(1<<k+1)<=y) k++;    return min(st[x][k],st[y-(1<<k)+1][k]);}inline bool check(int mid){    int i,l=A,r=B-mid+1;    int p=pre(1,len,root[l-1],root[r],rank[C]);    int q=nxt(1,len,root[l-1],root[r],rank[C]);    if(p>=1 && p<=len && mn(p,C)>=mid) return 1;    if(q>=1 && q<=len && mn(q,C)>=mid) return 1;    return 0;}int main(){//freopen("rand.txt","r",stdin);//freopen("WA.txt","w",stdout);    int i,j;    work();    fo(i,1,len)      build(1,len,root[i-1],root[i],rank[i]);    //fo(i,1,len) printf("root[%d]=%d\n",i,root[i]);    while(Q--)    {        scanf("%d%d%d%d",&A,&B,&C,&D);        int l=0,r=min(B-A+1,D-C+1);        while(l<r)        {            int mid=l+r+1>>1;            if(check(mid)) l=mid;            else r=mid-1;        }        printf("%d\n",l);    }    return 0;}


原创粉丝点击