BZOJ 4556: [Tjoi2016&Heoi2016]字符串
4556: [Tjoi2016&Heoi2016]字符串
Time Limit: 20 Sec Memory Limit: 128 MBSubmit: 789 Solved: 306
[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
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
1
2
2
2
HINT
Source
分析:
看到$lcp$基本第一想法就是后缀数组...
考虑二分答案,我们二分一个长度,判断是否存在$s[a,b]$的一个子串和$s[c,d]$的$lcp>=len$,判断的时候我们找出在$ran$数组中最大的包含$ran[c]$的区间,使得这一段区间的$height$最小值$>=len$,然后判断是否存在一个后缀的下标是区间$[a,b+1-len]$中的,因为要判断一个区间内部的元素存在性问题,所以我们用主席树维护下标存在与否,也就是主席树的下标代表的是后缀的开头位置,如果存在就设为$1$...
还是很好写的...
代码:
#include<algorithm> #include<iostream> #include<cstring> #include<cstdio> //by NeighThorn using namespace std; const int maxn=100000+5,maxm=7000000+5; int n,m,tot,sa[maxn],gs[maxn],wb[maxn],wv[maxn],ran[maxn],height[maxn]; int st[maxn][25],root[maxn],ls[maxm],rs[maxm],sum[maxm]; char s[maxn]; inline bool cmp(int *x,int a,int b,int l){ return x[a]==x[b]&&x[a+l]==x[b+l]; } inline void da(int *sa,int *x,int n,int m){ int i,j,p,*y=wb; for(i=0;i<m;i++) gs[i]=0; for(i=0;i<n;i++) gs[x[i]]++; for(i=1;i<m;i++) gs[i]+=gs[i-1]; for(i=n-1;~i;i--) sa[--gs[x[i]]]=i; for(j=1,p=1;p<n;j<<=1,m=p){ for(i=n-j,p=0;i<n;i++) y[p++]=i; for(i=0;i<n;i++) if(sa[i]>=j) y[p++]=sa[i]-j; for(i=0;i<n;i++) wv[i]=x[y[i]]; for(i=0;i<m;i++) gs[i]=0; for(i=0;i<n;i++) gs[wv[i]]++; for(i=1;i<m;i++) gs[i]+=gs[i-1]; for(i=n-1;~i;i--) sa[--gs[wv[i]]]=y[i]; p=1;swap(x,y);x[sa[0]]=0; for(i=1;i<n;i++) x[sa[i]]=cmp(y,sa[i],sa[i-1],j)?p-1:p++; } } inline void calcheight(int n){ int i,j,k=0; for(i=0;i<=n;i++) ran[sa[i]]=i; for(i=0;i<n;height[ran[i++]]=k) for(k?k--:233,j=sa[ran[i]-1];s[i+k]==s[j+k];k++); } inline bool cmp1(int x,int y){ return height[x]<height[y]; } inline void prework(void){ for(int i=1;i<=n;i++) st[i][0]=i; for(int j=1;j<=20;j++) for(int i=1;i+(1<<j-1)<=n;i++) st[i][j]=min(st[i+(1<<j-1)][j-1],st[i][j-1],cmp1); } inline int query(int x,int y){ if(x>y) return n; int d=y-x,k; for(k=20;k>=0;k--) if(d>>k||k==0) break; return height[min(st[x][k],st[y-(1<<k)+1][k],cmp1)]; } inline void change(int l,int r,int x,int &y,int pos){ y=++tot;sum[y]=sum[x]+1; if(l==r) return; int mid=(l+r)>>1;ls[y]=ls[x],rs[y]=rs[x]; if(pos<=mid) change(l,mid,ls[x],ls[y],pos); else change(mid+1,r,rs[x],rs[y],pos); } inline int query(int l,int r,int x,int y,int L,int R){ if(sum[y]-sum[x]==0) return 0; if(l==L&&R==r) return sum[y]-sum[x]>0; int mid=(l+r)>>1; if(R<=mid) return query(l,mid,ls[x],ls[y],L,R); else if(L>mid) return query(mid+1,r,rs[x],rs[y],L,R); else return query(l,mid,ls[x],ls[y],L,mid)|query(mid+1,r,rs[x],rs[y],mid+1,R); } inline bool check(int a,int b,int c,int len){ int l=1,r=ran[c],up=ran[c],down=ran[c]; while(l<=r){ int mid=(l+r)>>1; if(query(mid+1,ran[c])>=len) up=mid,r=mid-1; else l=mid+1; } l=ran[c];r=n; while(l<=r){ int mid=(l+r)>>1; if(query(ran[c]+1,mid)>=len) down=mid,l=mid+1; else r=mid-1; } if(query(0,n-1,root[up-1],root[down],a,b+1-len)) return true; return false; } inline int solve(int a,int b,int c,int d){ int l=1,r=min(d-c+1,b-a+1),ans=0; while(l<=r){ int mid=(l+r)>>1; if(check(a,b,c,mid)) ans=mid,l=mid+1; else r=mid-1; } return ans; } signed main(void){ scanf("%d%d%s",&n,&m,s); for(int i=0;i<n;i++) ran[i]=(int)s[i]; da(sa,ran,n+1,233);calcheight(n);prework(); for(int i=1;i<=n;i++) change(0,n-1,root[i-1],root[i],sa[i]); for(int i=1,a,b,c,d;i<=m;i++){ scanf("%d%d%d%d",&a,&b,&c,&d); printf("%d\n",solve(a-1,b-1,c-1,d-1)); } return 0; }
By NeighThorn