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
",solve(a-1,b-1,c-1,d-1));
}
return 0;
}
By NeighThorn