博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
洛谷P4482 [BJWC2018]Border 的四种求法 字符串,SAM,线段树合并,线段树,树链剖分,DSU on Tree...
阅读量:5154 次
发布时间:2019-06-13

本文共 5822 字,大约阅读时间需要 19 分钟。

原文链接https://www.cnblogs.com/zhouzhendong/p/LuoguP4482.html

题意

给定一个字符串 S,有 q 次询问,每次给定两个数 L,R ,求 S[L...R] 的最长前后缀。

$$q,|S|\leq 2 \times 10 ^ 5$$

题解

真是一道有趣的字符串题。

首先我们给 S 建出 SAM ,并用线段树合并预处理出每一个节点的 Right 集合。

我们要做的是找到最大的 $p$ 满足 $p<R, S[L...p] = S[R-p+L...R]$ ,即 $p<R, p - LCS(S[1...p],S[1...R]) + 1 \leq L$ 。($p - LCS(S[1...p],S[1...R]) + 1$ 相当于算出 $LCS(S[1...p],S[1...R])$ 的左端点位置。)

由于两个前缀的 LCS 就是他们在 parent 树上的 LCA 的 Max,所以我们来用parent树上的节点来表示这个式子的意义:设 S[1...x] 在 parent 树上对应的节点为 pos(x) ,则我们就是要找到这样一个 $p$ ,它满足 $p<R,p-Max(LCA(pos(p),pos[R]))+1\leq L$ 。

上式等价于 $p<\min(R,L+Max(LCA(pos(p),pos(R)))$ 。有 min 很棘手,我们先把他干掉:考虑到 pos(R) 的祖先的 Max 是随着深度变小而递减的,那么如果我们从 pos(R) 开始,一步一步跳 father ,则一定会经过一个临界点 $x$ ,满足:在这个点以及它之前跳到的任意一点 $a$,满足 $R\leq L+Max(pos(a))$;在这个点之后跳到的任意一点 $b$ ,满足 $R\geq L+Max(pos(a))$ 。对于点 $x$ 以及之前的点,我们只需要在点 $x$ 的 Right 集合中找到 $<R$ 的最大的值就好了,这个东西线段树上二分就可以了。

现在考虑剩下一半,这一半只需要满足:

$$p-Max(LCA(pos(p),pos[R]))+1\leq L$$

我们来看看询问的时候需要干什么:询问先处理掉临界点以下的,直接从临界点开始跳。对于跳到的每一个节点,我们要干什么呢?设当前节点为 c ,设它为 LCA ,那么我们只要在它的 Right 集合中找到 $<L+Max(c)$ 的最大值就好了,同样还是线段树上二分。如果设 s 为 c 的一个儿子,它的子树中包含了 pos(R) ,你会发现我多算了这个子树的贡献。但是稍加思索就可以得知,它的贡献会在子树中再算一遍,而在子树中的 Max 比较大,所以再算一次不会更优。

我们考虑对 parent 树进行树链剖分。

现在我们已经给他树链剖分了,所以我们可以把临界点到根路径划分成 $O(\log n)$ 段重链,而且每一段都是某条重链的前缀。

考虑把询问进行离线,对于每一条重链按照深度顺序从浅到深加入当前节点的贡献(用线段树维护),即当前集合的 Right 集合内的所有元素 减去当前节点的 Max 然后+1,在加入的同时回答询问。这样做的复杂度显然是不对的!但是我们只要改一改就对了。对于一个节点 d 的重儿子的 Right 集合,d 为 LCA 时一定不比 重儿子为 LCA 时优,所以我们不加重儿子的 Right 集合的贡献,留到重儿子里面加。这样的话,每次询问的时候,询问的前缀重链的最深点要特殊处理。为什么这样时间复杂度是对的?这个东西其实就是 DSU on Tree 的复杂度,具体来说每一个点的对时间复杂度的贡献次数是它到根路径上轻重链切换的次数。

于是我们得到了一个 $O((|S|+q)\log^2 |S|)$ 的做法。

代码

#pragma GCC optimize(2)#include 
#define clr(x) memset(x,0,sizeof (x))#define For(i,a,b) for (int i=a;i<=b;i++)#define Fod(i,b,a) for (int i=b;i>=a;i--)using namespace std;typedef long long LL;LL read(){ LL x=0,f=0; char ch=getchar(); while (!isdigit(ch)) f|=ch=='-',ch=getchar(); while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return f?-x:x;}const int N=200005*2;int n,Q;int pnode[N];char s[N];vector
id;namespace Mseg{ const int S=N*25; int ls[S],rs[S],size; void Init(){ size=0,clr(ls),clr(rs); } void Ins(int &rt,int L,int R,int x){ if (!rt) rt=++size; if (L==R) return; int mid=(L+R)>>1; if (x<=mid) Ins(ls[rt],L,mid,x); else Ins(rs[rt],mid+1,R,x); } int Merge(int a,int b,int L,int R){ if (!a||!b) return a+b; int rt=++size; if (L
>1; ls[rt]=Merge(ls[a],ls[b],L,mid); rs[rt]=Merge(rs[a],rs[b],mid+1,R); } return rt; } void GetR(int rt,int L,int R,vector
&v){ if (!rt) return; if (L==R) return (void)v.push_back(L); int mid=(L+R)>>1; GetR(ls[rt],L,mid,v); GetR(rs[rt],mid+1,R,v); } int Getpre(int rt,int L,int R,int xR){//
=xR) return 0; if (L==R) return L; int mid=(L+R)>>1,v=Getpre(rs[rt],mid+1,R,xR); if (v) return v; else return Getpre(ls[rt],L,mid,xR); }}namespace SAM{ struct Node{ int Next[26],fa,Max,pos; }t[N]; int last,root,size; int rt[N],id[N]; void Init(){ clr(t),Mseg::Init(); last=root=size=1; } void extend(int c,int ps){ int p=last,np=++size,q,nq; t[np].Max=t[p].Max+1,t[np].pos=ps,pnode[ps]=np; Mseg::Ins(rt[np],1,n,ps); for (;p&&!t[p].Next[c];p=t[p].fa) t[p].Next[c]=np; if (!p) t[np].fa=1; else { q=t[p].Next[c]; if (t[p].Max+1==t[q].Max) t[np].fa=q; else { nq=++size; t[nq]=t[q],t[nq].Max=t[p].Max+1,t[nq].pos=ps; t[np].fa=t[q].fa=nq; for (;p&&t[p].Next[c]==q;p=t[p].fa) t[p].Next[c]=nq; } } last=np; } void Sort(){ static int tax[N]; clr(tax); For(i,1,size) tax[t[i].Max]++; For(i,1,size) tax[i]+=tax[i-1]; For(i,1,size) id[tax[t[i].Max]--]=i; } void build(){ Sort(); Fod(i,size,2){ int x=id[i],f=t[x].fa; rt[f]=Mseg::Merge(rt[f],rt[x],1,n); } }}using SAM::t;vector
e[N],qs[N];int fa[N][20],son[N],size[N],top[N],aI[N],Time;void dfs(int x){ fa[x][0]=t[x].fa,size[x]=1,son[x]=0; For(i,1,19) fa[x][i]=fa[fa[x][i-1]][i-1]; for (auto y : e[x]){ dfs(y); size[x]+=size[y]; if (!son[x]||size[y]>size[son[x]]) son[x]=y; }}void Get_Top(int x,int Top){ top[x]=Top,aI[++Time]=x; if (son[x]) Get_Top(son[x],Top); for (auto y : e[x]) if (y!=son[x]) Get_Top(y,y);}struct que{ int L,R,ans; que(){} que(int _L,int _R){ L=_L,R=_R; }}q[N];void ins_q(int L,int R,int id){ int x=pnode[R]; Fod(i,19,0) if (L+t[fa[x][i]].Max-1>=R) x=fa[x][i]; q[id].ans=max(q[id].ans,Mseg::Getpre(SAM::rt[x],1,n,R)); x=fa[x][0]; while (x){ q[id].ans=max(q[id].ans,Mseg::Getpre(SAM::rt[x],1,n,L+t[x].Max)); assert(L+t[x].Max<=R); qs[x].push_back(id),x=fa[top[x]][0]; }}namespace Seg{ const int S=N*2*4; int ls[N],rs[N],Max[N],size; void Init(){ while (size) ls[size]=rs[size]=Max[size]=0,size--; } void Ins(int &rt,int L,int R,int x,int v){ if (!rt) rt=++size; Max[rt]=max(Max[rt],v); if (L==R) return; int mid=(L+R)>>1; if (x<=mid) Ins(ls[rt],L,mid,x,v); else Ins(rs[rt],mid+1,R,x,v); } int Query(int rt,int L,int R,int xL,int xR){ if (!rt||L>xR||R
>1; return max(Query(ls[rt],L,mid,xL,xR) ,Query(rs[rt],mid+1,R,xL,xR)); }}void solve(){ int sz=SAM::size,root; For(i,1,sz){ if (i==1||!son[aI[i-1]]) Seg::Init(),root=0; int x=aI[i]; for (auto j : qs[x]) q[j].ans=max(q[j].ans,Seg::Query(root,1,n,1,q[j].L)); id.clear(),id.push_back(t[x].pos); for (auto y : e[x]) if (y!=son[x]) Mseg::GetR(SAM::rt[y],1,n,id); for (auto y : id) Seg::Ins(root,1,n,y-t[x].Max+1,y); }}int main(){ scanf("%s",s+1),n=strlen(s+1); SAM::Init(); For(i,1,n) SAM::extend(s[i]-'a',i); SAM::build(); For(i,2,SAM::size) e[t[i].fa].push_back(i); dfs(1),Time=0,Get_Top(1,1),Q=read(); For(i,1,Q){ q[i].L=read(),q[i].R=read(),q[i].ans=0; ins_q(q[i].L,q[i].R,i); } solve(); For(i,1,Q) printf("%d\n",max(0,q[i].ans-q[i].L+1)); return 0;}

  

 

转载于:https://www.cnblogs.com/zhouzhendong/p/LuoguP4482.html

你可能感兴趣的文章
CSS选项卡
查看>>
HDOJ1203 I NEED A OFFER!
查看>>
ZH奶酪:自然语言处理工具LTP语言云调用方法
查看>>
.NET中将图片文件流转成Base64字符串的实现
查看>>
js如何操作或是更改sass里的变量
查看>>
BZOJ1419: Red is good
查看>>
POJ 3308 Paratroopers (对数转换+最小点权覆盖)
查看>>
rendering omni shadow in one pass.
查看>>
No repository found containing,eclipse 自动更新erro 解决
查看>>
iOS设计模式之单例模式
查看>>
MySQL面试题中:主从同步的原理
查看>>
HTTP和WebSocket协议(二)
查看>>
项目练习(二)—微博数据结构化
查看>>
尤金·卡巴斯基:卡巴斯基实验室调查内网遭黑客攻击事件
查看>>
android之Handler Runnable实现倒计时
查看>>
POS Tagging 标签类型查询表(Penn Treebank Project)
查看>>
Cookie/Session机制详解
查看>>
搭建Docker环境---Docker概述
查看>>
UVa 11383 少林决胜(二分图最佳完美匹配)
查看>>
ISP DSP的不同
查看>>