题目描述
佳媛姐姐过生日的时候,她的小伙伴从某东上买了一个生日礼物。生日礼物放在一个神奇的箱子中。箱子外边写了一个长为n的字符串s,和m个问题。佳媛姐姐必须正确回答这m个问题,才能打开箱子拿到礼物,升职加薪,出任CEO,嫁给高富帅,走上人生巅峰。每个问题均有a,b,c,d四个参数,问你子串s[a…b]的所有子串和s[c…d]的最长公共前缀的长度的最大值是多少?佳媛姐姐并不擅长做这样的问题,所以她向你求助,你该如何帮助她呢?
输入输出格式
输入格式:
输入的第一行有两个正整数
n
,
m
n,m
n,m,分别表示字符串的长度和询问的个数。接下来一行是一个长为
n
n
n的字符串。接下来
m
m
m行,每行有4个数
a
,
b
,
c
,
d
a,b,c,d
a,b,c,d,表示询问
s
[
a
.
.
b
]
s[a..b]
s[a..b]的所有子串和
s
[
c
.
.
d
]
s[c..d]
s[c..d]的最长公共前缀的最大值。
输出格式:
对于每一次询问,输出答案。
输入输出样例
输入样例#1:
5 5
aaaaa
1 1 1 5
1 5 1 1
2 3 2 3
2 4 2 3
2 3 2 4
输出样例#1:
1
1
2
2
2
说明
对于10%的数据, 1 < = n , m < = 300 1<=n,m<=300 1<=n,m<=300
对于40%的数据, 1 < = n , m < = 3000 1<=n,m<=3000 1<=n,m<=3000,字符串中仅有 a , b a,b a,b
对于100%的数据, 1 < = n , m < = 100000 1<=n,m<=100000 1<=n,m<=100000,字符串中仅有小写英文字母, a < = b , c < = d , 1 < = a , b , c , d < = n a<=b,c<=d,1<=a,b,c,d<=n a<=b,c<=d,1<=a,b,c,d<=n\
分析:
设
S
=
s
[
a
.
.
b
]
S=s[a..b]
S=s[a..b],显然只有
S
S
S的后缀才对答案有贡献。我们先对
s
s
s建一个后缀数组,相当于求与以
c
c
c开头的后缀的与
S
S
S的后缀的
l
c
p
lcp
lcp最大值(大概可以这样理解,虽然有边界问题)。
考虑二分一个答案,显然这个答案的上限是
m
i
n
(
b
−
a
+
1
,
d
−
c
+
1
)
min(b-a+1,d-c+1)
min(b−a+1,d−c+1),对于一个答案
m
i
d
mid
mid,可以二分出与
c
c
c这个后缀的
l
c
p
lcp
lcp大于等于
m
i
d
mid
mid的区间。显然后缀排序后,一个后缀与其他后缀的
l
c
p
lcp
lcp是一个单峰函数。
加入二分出来的区间为
[
l
c
,
r
c
]
[lc,rc]
[lc,rc],而
S
S
S串中只有前
[
a
,
b
−
m
i
d
+
1
]
[a,b-mid+1]
[a,b−mid+1]个后缀长度超过
m
i
d
mid
mid,相当于查询在排名为
l
c
lc
lc到
r
c
rc
rc的后缀中,有没有一个后缀是原串的第
[
a
,
b
−
m
i
d
+
1
]
[a,b-mid+1]
[a,b−mid+1]位。我们可以以排名为下标建可持久化权值线段
树即可。
要先预处理
l
o
g
log
log,因为c++自带的求
l
o
g
log
log采用的是二分,复杂度是
O
(
l
o
g
n
)
O(logn)
O(logn)的。
所以总复杂度是
O
(
n
l
o
g
2
n
)
O(nlog^2n)
O(nlog2n)级别的。
代码:
// luogu-judger-enable-o2
#include <iostream>
#include <cstdio>
#include <cmath>
const int maxn=1e5+7;
using namespace std;
int n,m,cnt;
int x[maxn],y[maxn],c[maxn],root[maxn],Log2[maxn];
char s[maxn];
struct node{
int l,r,data;
}t[maxn*31];
struct suffix_array{
int h[maxn][20],rank[maxn],sa[maxn];
void getsa()
{
int m=1000;
for (int i=1;i<=m;i++) c[i]=0;
for (int i=1;i<=n;i++) x[i]=s[i];
for (int i=1;i<=n;i++) c[x[i]]++;
for (int i=1;i<=m;i++) c[i]+=c[i-1];
for (int i=n;i>0;i--) sa[c[x[i]]--]=i;
for (int k=1;k<=n;k<<=1)
{
int num=0;
for (int i=n-k+1;i<=n;i++) y[++num]=i;
for (int i=1;i<=n;i++) if (sa[i]>k) y[++num]=sa[i]-k;
for (int i=1;i<=m;i++) c[i]=0;
for (int i=1;i<=n;i++) c[x[i]]++;
for (int i=1;i<=m;i++) c[i]+=c[i-1];
for (int i=n;i>0;i--) sa[c[x[y[i]]]--]=y[i],y[i]=0;
swap(x,y);
num=1;
x[sa[1]]=1;
for (int i=2;i<=n;i++)
{
if ((y[sa[i]]!=y[sa[i-1]]) || (y[sa[i]+k]!=y[sa[i-1]+k]))
{
x[sa[i]]=++num;
}
else x[sa[i]]=num;
}
if (num>=n) break;
m=num;
}
for (int i=1;i<=n;i++) rank[i]=x[i];
}
void getheight()
{
int k=0;
for (int i=1;i<=n;i++)
{
if (k) k--;
int j=sa[rank[i]-1];
while ((i+k<=n) && (j+k<=n) && (s[i+k]==s[j+k])) k++;
h[rank[i]][0]=k;
}
int c=1;
for (int j=1;j<20;j++)
{
for (int i=1;i<=n;i++)
{
if (i+c>n) h[i][j]=h[i][j-1];
else h[i][j]=min(h[i][j-1],h[i+c][j-1]);
}
c<<=1;
}
}
int lcp(int x,int y)
{
x=rank[x],y=rank[y];
if (x>y) swap(x,y);
x++;
int k=Log2[y-x+1];
return min(h[x][k],h[y-(1<<k)+1][k]);
}
}a;
void ins(int &p,int q,int l,int r,int x)
{
if (!p) p=++cnt;
t[p].data=t[q].data+1;
if (l==r) return;
int mid=(l+r)/2;
if (x<=mid) t[p].r=t[q].r,ins(t[p].l,t[q].l,l,mid,x);
else t[p].l=t[q].l,ins(t[p].r,t[q].r,mid+1,r,x);
}
int query(int p,int q,int l,int r,int x,int y)
{
if ((l==x) && (r==y)) return t[p].data-t[q].data;
if (t[p].data-t[q].data==0) return 0;
int mid=(l+r)/2;
if (y<=mid) return query(t[p].l,t[q].l,l,mid,x,y);
else if (x>mid) return query(t[p].r,t[q].r,mid+1,r,x,y);
else return query(t[p].l,t[q].l,l,mid,x,mid)+query(t[p].r,t[q].r,mid+1,r,mid+1,y);
}
int find1(int d,int x)
{
int l=1,r=x-1,ans=x;
while (l<=r)
{
int mid=(l+r)/2;
if (a.lcp(a.sa[mid],a.sa[x])>=d) r=mid-1,ans=mid;
else l=mid+1;
}
return ans;
}
int find2(int d,int x)
{
int l=x+1,r=n,ans=x;
while (l<=r)
{
int mid=(l+r)/2;
if (a.lcp(a.sa[x],a.sa[mid])>=d) l=mid+1,ans=mid;
else r=mid-1;
}
return ans;
}
int main()
{
scanf("%d%d",&n,&m);
scanf("%s",s+1);
a.getsa();
a.getheight();
for (int i=1;i<=n;i++)
{
ins(root[i],root[i-1],1,n,a.sa[i]);
}
for (int i=1;i<=n;i++) Log2[i]=trunc(log(i+0.5)/log(2));
for (int i=1;i<=m;i++)
{
int x,y,A,B;
scanf("%d%d%d%d",&x,&y,&A,&B);
int l=1,r=min(B-A+1,y-x+1),ans=0;
while (l<=r)
{
int mid=(l+r)/2;
int lc=find1(mid,a.rank[A]),rc=find2(mid,a.rank[A]);
if (query(root[rc],root[lc-1],1,n,x,y-mid+1)) ans=mid,l=mid+1;
else r=mid-1;
}
printf("%d\n",ans);
}
}