[BJWC2018]Border 的四种求法 题解
SAM+线段树合并+树链剖分
感谢 gls 的教导!!!
Statement
多次询问区间 Border 长度。
\(n,q\le 2\times 10^5\)
Solution
每一个询问就是 \(\min\{i|lcp(l,i)\ge r-i+1,i\in(l,r]\}\) ,其中 \(lcp(i,j)\) 表示 \(suf[i]\) 和 \(suf[j]\) 的最长公共前缀。
移一下项,\(lcp(l,i)+i\ge r+1\)
看到 \(lcp\) ,可以想到对原串的反串建立 SAM,提出来一棵 parent 树,那么 \(lcp(i,j)\) 便对应 \(n-i+1,n-j+1\) 在树上对应节点的 \(lca\) 的 \(len\)
考虑暴力做法,每次从 \(n-l+1\) 对应节点一个一个点向树根跳,钦定当前跳到的点 \(x\) 就是 \(lca\)
每次即是询问 \(x\) 的子树中最小满足条件的点
一个问题,这里的子树应不应该去掉上一个跳到的点的子树呢
其实无所谓,显然上一个点的 \(len >\) 当前点的 \(len\),所以当前点算出来的答案可行那么在之前的点子树中肯定也可行
考虑如何询问最小的满足 \(lcp(l,i)+i\ge r+1\) 的子树中的 \(i\),容易想到线段树合并后线段树上二分。线段树节点 \([L,R]\) 记录当前子树中,\(lcp+i\) 最大值即可。
注意到每次在合并子树的时候,应将子树中的 \(lcp\) 都按当前点的 \(len\) 计算
暴力适用于原串随机,即 parent 树树高 log,于是考虑树链剖分
树剖后,容易发现每一次询问的其实都是一些树链的前缀
设 \(y\) 是 \(x\) 向上跳重链时进入当前重链的点,则贡献来自两种地方:
- \(y\) 的子树
- \(y\) 往上直到重链顶的那些点,以及它们的轻子树
我们两种贡献分两次算,因为线段树合并顺序有点不同
第一种直接合并子树然后做就可以了
第二种,先合并掉轻子树,然后把自己并给重儿子,最后递归重儿子。这样做到了一个前缀的效果
容易想到可以把询问离线,挂到 \(log\) 条重链的某个点上,每次合并完信息处理一下即可
\(O((n+q)\log n)\)
Code
时刻小心自己是反串
#include<bits/stdc++.h>
using namespace std;
const int N = 4e5+5;
char buf[1<<23],*p1=buf,*p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
int read(){
int s=0,w=1; char ch=getchar();
while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
while(isdigit(ch))s=s*10+(ch^48),ch=getchar();
return s*w;
}
struct SAM{
int ch[N][26],link[N],len[N];
int las,tot;
char s[N];
SAM(){las=tot=1;}
void insert(int x){
int p=las;
len[las=++tot]=len[p]+1;
for(;p&&!ch[p][x];p=link[p])ch[p][x]=las;
if(!p)return link[las]=1,void();
int q=ch[p][x];
if(len[q]==len[p]+1)
return link[las]=q,void();
len[++tot]=len[p]+1,link[tot]=link[q];
memcpy(ch[tot],ch[q],sizeof(ch[tot]));
for(;ch[p][x]==q;p=link[p])ch[p][x]=tot;
link[las]=link[q]=tot;
}
}sam;
struct Segment_Tree{
#define ls t[rt].l
#define rs t[rt].r
#define mid ((l+r)>>1)
struct Tree{
int l,r,ori,mx,tg;
void init(){l=r=ori=mx=0,tg=-1;}
}t[N*80];
int siz=0;
void pushup(int rt){
t[rt].mx=max(t[ls].mx,t[rs].mx);
t[rt].ori=max(t[ls].ori,t[rs].ori);
}
void pushdown(int rt){
if(t[rt].tg==-1)return ;
if(ls)t[ls].mx=t[ls].ori+t[rt].tg,t[ls].tg=t[rt].tg;
if(rs)t[rs].mx=t[rs].ori+t[rt].tg,t[rs].tg=t[rt].tg;
t[rt].tg=-1;
}
void insert(int l,int r,int& rt,int id,int v){
if(!rt)rt=++siz,t[rt].init();
if(l==r)return t[rt].ori=l,t[rt].mx=l+v,void();
pushdown(rt);
if(id<=mid)insert(l,mid,ls,id,v);
else insert(mid+1,r,rs,id,v);
pushup(rt);
}
void change(int l,int r,int rt,int L,int R,int v){
if(!rt)return ;
if(L<=l&&r<=R){
t[rt].tg=v,t[rt].mx=t[rt].ori+v;
return ;
}
pushdown(rt);
if(L<=mid)change(l,mid,ls,L,R,v);
if(mid<R)change(mid+1,r,rs,L,R,v);
pushup(rt);
}
int query(int l,int r,int rt,int L,int R,int v){
if(!rt||r<L||R<l)return 1e9;
if(L<=l&&r<=R){
if(l==r)return t[rt].mx>=v?l:1e9;
pushdown(rt);
if(t[ls].mx>=v)return query(l,mid,ls,L,R,v);
if(t[rs].mx>=v)return query(mid+1,r,rs,L,R,v);
return 1e9;
}
pushdown(rt);
int tmp=query(l,mid,ls,L,R,v);
if(tmp==1e9)return query(mid+1,r,rs,L,R,v);
return tmp;
}
int merge(int l,int r,int p,int q){
if(!p||!q)return p+q;
pushdown(p),pushdown(q);
t[p].l=merge(l,mid,t[p].l,t[q].l);
t[p].r=merge(mid+1,r,t[p].r,t[q].r);
return pushup(p),p;
}
#undef ls
#undef rs
#undef mid
}seg;
struct Query{int l,r,id;};
int f[N],siz[N],son[N],bot[N],dfn[N],rev[N],top[N];
int pos[N],rot[N],ans[N],l[N],r[N];
vector<Query>ques[N];
vector<int>Edge[N];
int n,q,tim;
void dfs1(int u){
for(auto v:Edge[u])
siz[v]=1,dfs1(v),siz[u]+=siz[v],
(siz[v]>siz[son[u]]&&(son[u]=v,1));
}
void dfs2(int u,int tp){
top[u]=tp,rev[dfn[u]=++tim]=u;
if(son[u])
dfs2(son[u],tp),bot[u]=bot[son[u]];
else bot[u]=u;
for(auto v:Edge[u])if(v^son[u])dfs2(v,v);
}
void calc1(int u){
for(auto v:Edge[u])if(v^son[u]){
calc1(v),seg.change(1,n,rot[bot[v]],1,n,sam.len[u]);
rot[u]=seg.merge(1,n,rot[u],rot[bot[v]]);
}
for(auto v:ques[u])
ans[v.id]=min(ans[v.id],seg.query(1,n,rot[u],v.l+1,v.r,v.r+1));
if(son[u])rot[son[u]]=seg.merge(1,n,rot[son[u]],rot[u]),calc1(son[u]);
}
void calc2(int u){
if(son[u])calc2(son[u]),rot[u]=seg.merge(1,n,rot[u],rot[son[u]]);
for(auto v:Edge[u])if(v^son[u])
calc2(v),rot[u]=seg.merge(1,n,rot[u],rot[v]);
seg.change(1,n,rot[u],1,n,sam.len[u]);
for(auto v:ques[u])
ans[v.id]=min(ans[v.id],seg.query(1,n,rot[u],v.l+1,v.r,v.r+1));
}
signed main(){
scanf("%s",sam.s+1),n=strlen(sam.s+1);
reverse(sam.s+1,sam.s+1+n);
for(int i=1;i<=n;++i)sam.insert(sam.s[i]-'a'),pos[i]=sam.las;
for(int i=2;i<=sam.tot;++i)Edge[f[i]=sam.link[i]].push_back(i);
dfs1(siz[1]=1),dfs2(1,1),q=read();
for(int i=1,u;i<=q;++i){
l[i]=read(),r[i]=read(),u=pos[n-l[i]+1],ans[i]=1e9;
while(u)ques[u].push_back((Query){l[i],r[i],i}),u=f[top[u]];
}
for(int i=1;i<=n;++i)
seg.insert(1,n,rot[pos[i]],n+1-i,sam.len[pos[i]]);
calc1(1),memset(rot,0,sizeof(rot)),seg.siz=0;
for(int i=1;i<=n;++i)
seg.insert(1,n,rot[pos[i]],n+1-i,sam.len[pos[i]]);
calc2(1);
for(int i=1;i<=q;++i)
printf("%d\n",max(0,r[i]+1-ans[i]));
return 0;
}