题意:Border就是一个串除了自己以外,最长的前缀使得它等于自己的一个后缀。现在给你一个串,每次问你
[
l
,
r
]
[l,r]
[l,r]的border长度。
数据范围:
1
≤
∣
S
∣
,
q
≤
2
×
1
0
5
1\leq |S|,q \leq 2\times 10^5
1≤∣S∣,q≤2×105(时限
5
s
5s
5s)
做法:我们考虑这个border的前缀,如果答案后缀的开头在
i
i
i,则要满足
l
c
p
(
l
,
i
)
+
i
>
r
lcp(l,i)+i>r
lcp(l,i)+i>r,然后我们发现这个式子很优美,我们把后缀树建出来,使用树剖维护,然后我们怎么办呢。我们发现这个
l
c
p
(
i
,
l
)
lcp(i,l)
lcp(i,l)就是
i
,
l
i,l
i,l后缀节点的lca,这个在树剖上比较难维护。但是我们可以进行讨论,如果两个点在一条重链上,肯定是深度浅的点作为lca,所以就在线段树上维护两个东西,一个是
l
c
p
lcp
lcp是
l
l
l走上来的,另一个是
i
i
i走上来的。询问可以离线,从
l
l
l的位置一路往上更新,每次遇到重链,来一次单点修改,插入一个
r
r
r(或者
r
−
l
c
p
r-lcp
r−lcp)。
i
i
i则从下到上,每遇到重链就在这个节点上面查
r
−
l
c
p
r-lcp
r−lcp的最小值,下面查
r
r
r的最小值,只要遇到最小值就删掉,并更新答案,因为我们想要
i
i
i尽量小。
最后我们一波边扫
i
i
i,边插入询问。底层用堆什么的维护一下,就行了。
时间复杂度:
O
(
n
l
o
g
2
(
n
)
)
O(nlog^2(n))
O(nlog2(n))
代码:
#include<iostream>
#include<cstring>
#include<cassert>
#include<cmath>
#include<map>
#include<set>
#include<queue>
#include<stack>
#include<cstdio>
#include<vector>
#include<time.h>
#include<algorithm>
using namespace std;
#define REP(i,x,y) for(int i=x;i<=y;i++)
#define rep(i,n) REP(i,1,n)
#define rep0(i,n) REP(i,0,n-1)
#define repG(i,x) for(int i=pos[x];~i;i=e[i].next)
#define ll long long
#define db double
const int N=4e5+7;
const int INF=1e9+7;
struct sam{int a[26];int p,l;}t[N];
struct edge{int v,next;}e[N*2];
struct wt{int l,r,id;}Q[N];
char s[N];
int pos[N],w[N],as[N],id[N],dw[N];
int n,q,last=0,cnt=0,num=0,tot=0;
void add(int x,int y){e[num]=(edge){y,pos[x]}; pos[x]=num++;}
int inss(int x){
t[++cnt].l=t[last].l+1;
int i;
for(i=last;~i;i=t[i].p){
if(t[i].a[x])break;
t[i].a[x]=cnt;
}
if(i<0){t[cnt].p=0; last=cnt; return last;}
int p=t[i].a[x];
if(t[p].l==t[i].l+1){t[cnt].p=p; last=cnt; return last;}
int nm=cnt;
t[++cnt]=t[p];
t[nm].p=cnt;
t[p].p=cnt;
t[cnt].l=t[i].l+1;
for(;~i;i=t[i].p){
if(t[i].a[x]!=p)break;
t[i].a[x]=cnt;
}
last=nm;
return last;
}//建立sam
bool cmp(wt a,wt b){return a.l<b.l;}
int fa[N],top[N],sz[N],ind[N],dep[N],son[N];
void dfs1(int x,int f,int d){
dep[x]=d;
sz[x]=1;
fa[x]=f;
int maxn=0,id;
repG(i,x){
dfs1(e[i].v,x,d+1);
if(sz[e[i].v]>maxn){
maxn=sz[e[i].v];
id=e[i].v;
}
sz[x]+=sz[e[i].v];
}
son[x]=id;
}
void dfs2(int x,int tp){
top[x]=tp;
ind[x]=++tot;
id[tot]=x;
if(sz[x]==1)return;
dfs2(son[x],tp);
repG(i,x)if(e[i].v!=fa[x]&&e[i].v!=son[x])dfs2(e[i].v,e[i].v);
}
struct pir{
int v,x;
friend bool operator <(pir x,pir y){if(x.v!=y.v)return x.v>y.v; return x.x<y.x;}
friend bool operator ==(pir x,pir y){return (x.x==y.x)&(x.v==y.v);}
};
priority_queue<pir>q1[N],q2[N],sd1[N],sd2[N];//q1,q2分别表示两种情况,其余两个是用来删除
struct Tree{int l,r,m1,m2;}tree[N*4];
void build(int l,int r,int p){
tree[p]=(Tree){l,r,INF,INF};
if(l==r){
q1[l].push((pir){INF,0});
q2[l].push((pir){INF,0});
return;
}
int mid=(l+r)>>1;
build(l,mid,p*2);
build(mid+1,r,p*2+1);
}
void update(int p){
tree[p].m1=min(tree[p*2].m1,tree[p*2+1].m1);
tree[p].m2=min(tree[p*2].m2,tree[p*2+1].m2);
}
void ins(int p,int x,int y,int d){
int l=tree[p].l,r=tree[p].r;
if(l==r){
q1[l].push((pir){y,d});//未确定lca(lcp)
q2[l].push((pir){y-t[id[x]].l,d});//确定了lca(lcp)
tree[p].m1=q1[l].top().v;
tree[p].m2=q2[l].top().v;
return;
}
int mid=(l+r)>>1;
if(x<=mid)ins(p*2,x,y,d);
else ins(p*2+1,x,y,d);
update(p);
}
void del(int p,int x,int y,int d){
int l=tree[p].l,r=tree[p].r;
if(l==r){//对应删除
sd1[l].push((pir){y,d});
sd2[l].push((pir){y-t[id[x]].l,d});
while(sd1[l].top()==q1[l].top())sd1[l].pop(),q1[l].pop();
while(sd2[l].top()==q2[l].top())sd2[l].pop(),q2[l].pop();
tree[p].m1=q1[l].top().v;
tree[p].m2=q2[l].top().v;
return;
}
int mid=(l+r)>>1;
if(x<=mid)del(p*2,x,y,d);
else del(p*2+1,x,y,d);
update(p);
}
int check1(int p,int L,int R,int d){//分别求两种情况最小值
int l=tree[p].l,r=tree[p].r;
if(L<=l&&R>=r){
if(tree[p].m1>=d)return 0;
if(l==r)return q1[l].top().x;
if(tree[p*2].m1<d)return check1(p*2,L,R,d);
return check1(p*2+1,L,R,d);
}
int mid=(l+r)>>1;
if(L<=mid){
if(R>mid){
int q=check1(p*2,L,R,d);
return q?q:check1(p*2+1,L,R,d);
}
return check1(p*2,L,R,d);
}
return check1(p*2+1,L,R,d);
}
int check2(int p,int L,int R,int d){
int l=tree[p].l,r=tree[p].r;
if(L<=l&&R>=r){
if(tree[p].m2>=d)return 0;
if(l==r)return q2[l].top().x;
if(tree[p*2].m2<d)return check2(p*2,L,R,d);
return check2(p*2+1,L,R,d);
}
int mid=(l+r)>>1;
if(L<=mid){
if(R>mid){
int q=check2(p*2,L,R,d);
return q?q:check2(p*2+1,L,R,d);
}
return check2(p*2,L,R,d);
}
return check2(p*2+1,L,R,d);
}
void wins(int x,int r,int d){while(x)ins(1,ind[x],r,d),x=fa[top[x]];}
void wdel(int x,int r,int d){while(x)del(1,ind[x],r,d),x=fa[top[x]];}
int wac(int x,int cc){
while(x){
int tp=top[x],ed=dw[top[x]];
int gg=check1(1,ind[x],ind[ed],t[x].l+cc);
if(gg)return gg;
int hh=check2(1,ind[tp],ind[x],cc);
if(hh)return hh;
x=fa[top[x]];
}
return 0;
}
int main(){
scanf("%s%d",s+1,&q);
int n=strlen(s+1);
n++;
s[n]='a';
t[0].p=-1;
for(int i=n;i;i--)w[i]=inss(s[i]-'a')+1;
memset(pos,-1,sizeof(pos));
cnt++;
for(int i=cnt;i;i--)t[i]=t[i-1];
rep(i,cnt)t[i].p++;
rep(i,cnt)add(t[i].p,i);
rep(i,q)scanf("%d%d",&Q[i].l,&Q[i].r),Q[i].id=i;
rep(i,q)as[i]=Q[i].r+1;
sort(Q+1,Q+q+1,cmp);
dfs1(1,0,1);
dfs2(1,1);
build(1,cnt,1);
rep(i,cnt)dw[i]=i;
rep(i,cnt)if(dep[dw[top[i]]]<dep[i])dw[top[i]]=i;
int nw=1;
rep(i,n){
while(Q[nw].l<=i-1&&nw<=q)wins(w[Q[nw].l],Q[nw].r,nw),nw++;
int oo=wac(w[i],i);
while(oo)as[Q[oo].id]-=i,wdel(w[Q[oo].l],Q[oo].r,oo),oo=wac(w[i],i);
}
rep(i,q)printf("%d\n",as[i]);
return 0;
}