bzoj 4556 [Tjoi2016&Heoi2016]字符串

来源:互联网 发布:mac怎么播放wmv 编辑:程序博客网 时间:2024/06/11 12:18

4556: [Tjoi2016&Heoi2016]字符串

Time Limit: 20 Sec Memory Limit: 128 MB
Submit: 952 Solved: 374
[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


【分析】

看阿当学长的代码肝还肝了两个多小时 2333334666666

我们发现这道鬼畜的题用LCP不好搞,于是翻转一下,变成了求LCS蛤蛤。

把串翻转以后建出来一只后缀自动机,搞出来pre树。

我们惊奇的发现如果区间[a,b]中某个节点和d的节点在pre树上的LCA的深度为dep,那么dep可以来更新答案。

显然不能枚举[a,b]区间里每一个数,那么我们二分答案。

发现其实[c,d]串如果考虑LCS的话c没有什么卵用,只是用来卡答案上界的。设当前答案为 mid,那么我们从d所在pre树中的位置向上蹦跶到深度最小且满足 step>=mid 的位置,然后如果这个节点的子树中有[a,b]区间对应在pre树上的节点,那么该答案成立。判断可以通过权值线段树的合并来实现。

总复杂度 O(nlog2n)

我一直在想一个问题,为什么Markdown里面的代码片这么丑呢。


【代码】

//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 M=4000005;const int mxn=200005;char s[mxn];int ls[M],rs[M];int m,T,len,tot,p,q,np,nq,size;int b[mxn],C[mxn],root[mxn],pos[mxn],step[mxn],son[mxn][28],pre[mxn][22];inline void insert(int &x,int l,int r,int v){    x=(++size);    int mid=l+r>>1;    if(l==r) return;    if(v<=mid) insert(ls[x],l,mid,v);    else insert(rs[x],mid+1,r,v);}inline int merge(int x,int y){    if(!x) return y;    if(!y) return x;    int z=(++size);    ls[z]=merge(ls[x],ls[y]);    rs[z]=merge(rs[x],rs[y]);    return z;}inline bool find(int x,int l,int r,int L,int R){    if(!x) return 0;    if(l==L && r==R) return 1;    int mid=l+r>>1;    if(R<=mid) return find(ls[x],l,mid,L,R);    else if(L>mid) return find(rs[x],mid+1,r,L,R);    else return find(ls[x],l,mid,L,mid)||find(rs[x],mid+1,r,mid+1,R);}inline void sam(){    int i,j,c;    np=tot=1;    fo(i,1,len)    {        c=s[i]-'a'+1,p=np;        step[np=(++tot)]=step[p]+1;        pos[i]=np,insert(root[np],1,len,i);        while(p && !son[p][c])          son[p][c]=np,p=pre[p][0];        if(!p) {pre[np][0]=1;continue;}        q=son[p][c];        if(step[q]==step[p]+1)          pre[np][0]=q;        else        {            step[nq=(++tot)]=step[p]+1;            memcpy(son[nq],son[q],sizeof son[q]);            pre[nq][0]=pre[q][0];            pre[q][0]=pre[np][0]=nq;            while(p && son[p][c]==q)              son[p][c]=nq,p=pre[p][0];        }    }    fo(i,1,tot) b[step[i]]++;    fo(i,1,tot) b[i]+=b[i-1];    fo(i,1,tot) C[b[step[i]]--]=i;    for(i=tot;i>=1;i--)    {        int x=C[i],fa=pre[x][0];        root[fa]=merge(root[fa],root[x]);    }    fo(j,1,20) fo(i,1,tot) pre[i][j]=pre[pre[i][j-1]][j-1];}inline bool check(int mid,int x,int l,int r){    for(int i=20;i>=0;i--) if(step[pre[x][i]]>=mid) x=pre[x][i];    return find(root[x],1,len,l,r);}int main(){    int i,j,a,b,c,d;    scanf("%d%d",&len,&m);    scanf("%s",s+1);    reverse(s+1,s+len+1);    sam();    while(m--)    {        scanf("%d%d%d%d",&a,&b,&c,&d);        a=len-a+1,b=len-b+1,c=len-c+1,d=len-d+1;        swap(a,b),swap(c,d);        int l=0,r=min(d-c+1,b-a+1);        while(l<r)        {            int mid=(l+r>>1)+1;            if(check(mid,pos[d],a+mid-1,b)) l=mid;            else r=mid-1;        }        printf("%d\n",l);    }    return 0;}