题目描述:
给定一个长度为N的字符串s和M个询问, 每次询问一个区间[l, r]的最大字典序子串。
输出共M行, 第i行输出一个整数, 表示你的答案子串的起始位置。
1 ≤ N,M ≤ 200000
题目分析:
首先,区间最大字典序子串一定是一段后缀。
我们把询问离线,按右端点从小到大排序,然后从左到右扫,扫到位置
p
p
p,就回答
r
=
p
r=p
r=p的询问。
对于一个固定的右端点,随着左端点的增加,答案子串的起始位置单调不降。
对于两个后缀
s
[
l
1
,
r
]
s[l_1,r]
s[l1,r]和
s
[
l
2
,
r
]
s[l_2,r]
s[l2,r],若
l
1
<
l
2
l_1<l_2
l1<l2且
s
[
l
1
,
r
]
<
s
[
l
2
,
r
]
s[l_1,r]<s[l_2,r]
s[l1,r]<s[l2,r],那么随着
r
r
r的增加,前者恒小于后者,且前者的区间包含后者的区间,所以不可能作为答案。
更一般的,对于两个后缀
s
[
l
1
,
r
]
s[l_1,r]
s[l1,r]和
s
[
l
2
,
r
]
s[l_2,r]
s[l2,r],若
s
[
l
1
+
L
C
P
(
l
1
,
l
2
)
]
<
s
[
l
2
+
L
C
P
(
l
1
,
l
2
)
]
s[l_1+LCP(l_1,l_2)]<s[l_2+LCP(l_1,l_2)]
s[l1+LCP(l1,l2)]<s[l2+LCP(l1,l2)],那么当
r
<
l
2
+
L
C
P
(
l
1
,
l
2
)
r<l_2+LCP(l_1,l_2)
r<l2+LCP(l1,l2)时,有
s
[
l
1
,
r
]
>
s
[
l
2
,
r
]
s[l_1,r]>s[l_2,r]
s[l1,r]>s[l2,r],当
r
≥
l
2
+
L
C
P
(
l
1
,
l
2
)
r\ge l_2+LCP(l_1,l_2)
r≥l2+LCP(l1,l2),有
s
[
l
1
,
r
]
<
s
[
l
2
,
r
]
s[l_1,r]<s[l_2,r]
s[l1,r]<s[l2,r]
所以我们可以在扫的时候用set维护可能作为答案的串的起始位置,询问[l,r],就在set里lower_bound(l)。
这道题充分利用了后缀比较的单调性。嗯,好题(膜高三学长出的膜你题QWQ。。虽然感觉像搬的一样好?)。
Code:
#include <bits/stdc++.h>
#define maxn 200005
using namespace std;
typedef unsigned long long ULL;
const ULL seed = 37;
ULL h[maxn],pw[maxn];
int n,m,ans[maxn];
set<int>suf;
vector<int>del[maxn],G[maxn],stk;
vector<pair<int,int> >q[maxn];
char s[maxn];
ULL Hash(int i,int j){return h[j]-h[i-1]*pw[j-i+1];}
int LCP(int i,int j){
int l=0,r=min(n-i,n-j)+1,mid;
while(l<r){
mid=(l+r+1)>>1;
if(Hash(i,i+mid-1)==Hash(j,j+mid-1)) l=mid;
else r=mid-1;
}
return l;
}
void dfs(int u){
if(suf.find(u)==suf.end()) return;
suf.erase(u);
for(int v: G[u]) dfs(v);
}
int main()
{
scanf("%d%d%s",&n,&m,s+1);
for(int i=1,l,r;i<=m;i++) scanf("%d%d",&l,&r),q[r].push_back(make_pair(l,i));
pw[0]=1;
for(int i=1;i<=n;i++) h[i]=h[i-1]*seed+s[i],pw[i]=pw[i-1]*seed;
for(int i=1;i<=n;i++){
while(!stk.empty()){
int j=stk.back(),len=LCP(i,j);
if(s[j+len]>s[i+len]) break;
del[i+len].push_back(j);
G[i].push_back(j);
stk.pop_back();
}
stk.push_back(i),suf.insert(i);
for(int j: del[i]) dfs(j);
for(auto Q: q[i]) ans[Q.second]=*suf.lower_bound(Q.first);
}
for(int i=1;i<=m;i++) printf("%d\n",ans[i]);
}