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;
}