4556: [Tjoi2016&Heoi2016]字符串
Time Limit: 20 Sec Memory Limit: 128 MBSubmit: 1066 Solved: 429
[Submit][Status][Discuss]
Description
佳媛姐姐过生日的时候,她的小伙伴从某东上买了一个生日礼物。生日礼物放在一个神奇的箱子中。箱子外边写了一个长为n的字符串s,和m个问题。佳媛姐姐必须正确回答这m个问题,才能打开箱子拿到礼物,升职加薪,出任CEO,嫁给高富帅,走上人生巅峰。每个问题均有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
这个题的思路真是很精巧啊
感觉对后缀数组的认识又全面了些??唉 其实本质还是思想江化啊
说题解
借用一下neither_nor的图。
然后自然而然就会想到用SA来解决问题(蒟蒻还不会SAM)
暴力扫a->b肯定是不行的
那么可以怎么搞呢。。
BJ最开始YY了一种树套树解法:
。。。写了半天最后删掉了,发现好像需要可持久化一些东西,然后比较懵逼。。。
放弃梦想,去查题解
考虑可以二分这个长度len(BJ根本没想到 /捂脸熊)
然后怎么判呢~
考虑从c处向两侧的后缀扩展会得到所有满足条件的后缀
之后只需要判有没有a->b-len+1的后缀在这个区间就行了
这个怎么搞呢
考虑主席树有区间分割的功效
BJ是建了一个suffix为下标rank为权值的主席树
然后每次询问在固定权值范围内是否有返回值就行了
之后又去看了一下commoc的代码
他是以rank为下标SA为权值,询问每次是否有SA在a->b内
都可以
这个题调了整整三天,最后发现RMQ里面用的是log,然后愤怒改成log2。。
还有就是,预处理log2要比调用函数快。。。
#include<cmath>
#include<ctime>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<iomanip>
#include<vector>
#include<string>
#include<bitset>
#include<queue>
#include<set>
#include<map>
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch<='9'&&ch>='0'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
return x*f;
}
void print(int x)
{if(x<0)putchar('-'),x=-x;if(x>=10)print(x/10);putchar(x%10+'0');}
const int N=100100;
char s[N];
int n,ht[N],sa1[N],sa2[N],rk1[N],rk2[N],buc[N];
int *sa=sa1,*tp=sa2,*rk=rk1,*tmp=rk2;
void getsa()
{
register int i,j,k;
for(i=1;i<=n;++i)buc[s[i]]++;
for(i=1;i<=300;++i)buc[i]+=buc[i-1];
for(i=n;i;i--)sa[buc[s[i]]--]=i;
for(i=1;i<=n;++i)rk[sa[i]]=rk[sa[i-1]]+(s[sa[i]]!=s[sa[i-1]]);
for(k=1;k<=n;k<<=1)
{
for(i=1;i<=n;++i)buc[rk[sa[i]]]=i;
for(i=n;i;i--)if(sa[i]>k)tp[buc[rk[sa[i]-k]]--]=sa[i]-k;
for(i=n-k+1;i<=n;++i)tp[buc[rk[i]]--]=i;
bool flag=0;
for(i=1;i<=n;++i)
{
tmp[tp[i]]=tmp[tp[i-1]]+(rk[tp[i]]!=rk[tp[i-1]]||rk[tp[i]+k]!=rk[tp[i-1]+k]);
if(tmp[tp[i]]==n)flag=1;
}
swap(tmp,rk);swap(tp,sa);
if(flag)break;
}
k=0;
for(i=1;i<=n;++i)
{
j=rk[i]-1;
while(s[sa[rk[i]]+k]==s[sa[j]+k])k++;
ht[rk[i]]=k;if(k)k--;
}
}
int root[N];
struct president_tree{int w,ls,rs;}tr[N<<5];
int sz;
void insert(int &k,int x,int l,int r,int val)
{
k=++sz;
tr[k].w=tr[x].w+1;if(l==r)return ;
tr[k].ls=tr[x].ls;tr[k].rs=tr[x].rs;
int mid=(l+r)>>1;
val<=mid?insert(tr[k].ls,tr[x].ls,l,mid,val):insert(tr[k].rs,tr[x].rs,mid+1,r,val);
}
int mn[N<<1][20],bas[N];
void initial()
{
register int i,j;
for(i=1;i<=n;++i)insert(root[i],root[i-1],1,n,rk[i]);
for(i=1;i<=n;++i)mn[i][0]=ht[i];
for(j=1;(1<<j)<=n;++j)for(i=1;i+(1<<(j-1))<=n;++i)
mn[i][j]=min(mn[i][j-1],mn[i+(1<<(j-1))][j-1]);
for(i=1;i<=n;++i)bas[i]=(1<<(bas[i-1]+1))==i?bas[i-1]+1:bas[i-1];
}
inline int query_mn(int l,int r)
{int k=bas[r-l+1];return min(mn[l][k],mn[r-(1<<k)+1][k]);}
int query(int a,int b,int l,int r,int x,int y)
{
if(l>=x&&r<=y)return tr[b].w-tr[a].w;
int mid=(l+r)>>1;
if(x>mid)return query(tr[a].rs,tr[b].rs,mid+1,r,x,y);
if(y<=mid)return query(tr[a].ls,tr[b].ls,l,mid,x,y);
return query(tr[a].ls,tr[b].ls,l,mid,x,y)+query(tr[a].rs,tr[b].rs,mid+1,r,x,y);
}
bool check(int aim,int pos,int x,int y)
{
register int l,r,mid,L=rk[pos]+1,R=rk[pos];
if(ht[rk[pos]]>=aim)
{
l=2,r=rk[pos];
while(l<=r)
{
mid=(l+r)>>1;
query_mn(mid,rk[pos])<aim?l=mid+1:(r=mid-1,L=mid);
}
}
if(ht[rk[pos]+1]>=aim)
{
l=rk[pos]+1;r=n;
while(l<=r)
{
mid=(l+r)>>1;
query_mn(rk[pos]+1,mid)<aim?r=mid-1:(l=mid+1,R=mid);
}
}
return query(root[x],root[y],1,n,L-1,R);
}
void solve()
{
register int a,b,c,d,l,r,mid,res=0;
a=read();b=read();c=read();d=read();
if(a==c){print(min(b,d)-a+1);puts("");return ;}
l=1;r=b-a+1;
while(l<=r)
{
mid=(l+r)>>1;
check(mid,c,a-1,b-mid+1)?(l=mid+1,res=mid):r=mid-1;
}
res=min(res,d-c+1);
print(res);puts("");
}
int main()
{
n=read();register int Q=read();
scanf("%s",s+1);
getsa();
initial();
while(Q--){solve();}
return 0;
}