题目大意:维护一个字符串,支持插入字符和替换字符的操作,以及查询该字符串两个后缀的最长公共前缀长度
乍一看以为是后缀数组,然而并没有可持久化后缀数组(雾)
看题解才知道这是一道splay题,首先要对splay维护区间信息有一定了解
splay维护,插入字符,替换字符
而它的字树内所有儿子的中序遍历的hash值也可以通过splay维护
(这个推导式似乎烂大街了)
而后缀就是把i-1拎到根节点,然后把n+1拎到根节点的右儿子上,它的左儿子表示的就是hash值
至于如何查公共前缀呢?二分答案啊!询问的总时间是
这个二分答案的check函数 的满足条件并非常规的>=或者<=,而是==,所以为了防止正确答案被略掉,每次二分的过程中都记录一次mid作为答案,而加下来mid在查询范围内已经没有意义了,所以是r=mid-1而不是r=mid
我一开始插入打错了竟然还有80分,数据太水了hhhhh
#include <cstdio>
#include <cstring>
#include <algorithm>
#define il inline
#define ui unsigned int
#define ull unsigned long long
#define seed 13131
#define N 110000
#define root d[0].ch[1]
using namespace std;
char str[N],Q[10],q[10];
ull pw[N];
int n,m,tot;
struct SPLAY{int fa,ch[2],sz;ull hsh,val;}d[N];
int gc()
{
int rett=0,fh=1;char p=getchar();
while(p<'0'||p>'9'){if(p=='-')fh=-1;p=getchar();}
while(p>='0'&&p<='9'){rett=(rett<<3)+(rett<<1)+p-'0';p=getchar();}
return rett*fh;
}
il ull idx(char c){return c-'a'+1;}
il void con(int x,int ff,int p){d[x].fa=ff,d[ff].ch[p]=x;}
il int idf(int x){return d[d[x].fa].ch[0]==x?0:1;}
il int cre(ull w){tot++;d[tot].val=w,d[tot].sz=1,d[tot].hsh=w;return tot;}
il void pushup(int x)
{
d[x].sz=d[d[x].ch[0]].sz+d[d[x].ch[1]].sz+1;
d[x].hsh=d[d[x].ch[0]].hsh*pw[d[d[x].ch[1]].sz+1]+(ull)d[x].val*pw[d[d[x].ch[1]].sz]+d[d[x].ch[1]].hsh;
}
il void rot(int x)
{
int y=d[x].fa;int ff=d[y].fa;int px=idf(x);int py=idf(y);
con(d[x].ch[px^1],y,px),con(y,x,px^1),con(x,ff,py);
pushup(y),pushup(x);
}
void splay(int x,int to)
{
to=d[to].fa;
while(d[x].fa!=to){
int y=d[x].fa;
if(d[y].fa==to) rot(x);
else if(idf(x)==idf(y)){rot(y);rot(x);}
else{rot(x);rot(x);}
}
}
int build(int l,int r,int ff)
{
if(l>r) return 0;
int mid=(l+r)>>1;
int pos=cre(idx(str[mid]));
d[pos].fa=ff;
d[pos].ch[0]=build(l,mid-1,pos);
d[pos].ch[1]=build(mid+1,r,pos);
pushup(pos);
return pos;
}
int find_pos(int pos)
{
int x=root;
while(x)
{
if(d[d[x].ch[0]].sz>=pos) x=d[x].ch[0];
else{
pos-=d[d[x].ch[0]].sz;
if(pos==1) return x;
pos--,x=d[x].ch[1];
}
}
return x;
}
void ins(int pos,ull w)
{
int ff=find_pos(pos+1),x,y;splay(ff,root);
if(!d[ff].ch[1])
x=cre(w),con(x,ff,1),pushup(ff);
else{
x=d[ff].ch[1];
while(x)
if(d[x].ch[0]) x=d[x].ch[0];
else break;
y=cre(w),con(y,x,0),pushup(x);
splay(x,root);
}
}
void replac(int pos,ull w)
{
int x=find_pos(pos+1);splay(x,root);
d[x].val=w;pushup(x);
}
il ull get_hsh(int pos)
{
int x=find_pos(pos);
splay(x,root);
int y=find_pos(n+2);
splay(y,d[x].ch[1]);
return d[d[d[x].ch[1]].ch[0]].hsh;
}
il int check(int x,int y,int mid)
{
ull s1=0,s2=0,s3=0,s4=0;
s1=get_hsh(x);
if(x+mid<=n) s2=get_hsh(x+mid);
s3=get_hsh(y);
if(y+mid<=n) s4=get_hsh(y+mid);
return (s1-s2==(s3-s4)*pw[y-x])?1:0;
}
int Query(int a,int b)
{
if(a>b){int t=a;a=b;b=t;}
int l=1,r=n-b+1,ans=0;
while(l<=r){
int mid=(l+r)>>1;
if(check(a,b,mid)) ans=mid,l=mid+1;
else r=mid-1;
}
return ans;
}
void stt()
{
n=strlen(str+1);pw[0]=1;
for(int i=1;i<=100000;i++) pw[i]=pw[i-1]*seed;
int x=cre(0),y=cre(0),z;
con(x,0,1),con(y,1,1);
z=build(1,n,y);
con(z,y,0);
pushup(y),pushup(x);
}
int main()
{
scanf("%s",str+1);
stt();
scanf("%d",&m);
int x,y;
for(int i=1;i<=m;i++)
{
scanf("%s",Q);
if(Q[0]=='Q'){
x=gc(),y=gc();
printf("%d\n",Query(x,y));
}else if(Q[0]=='I'){
x=gc();scanf("%s",q);
ins(x,idx(q[0]));
n++;
}else{
x=gc();scanf("%s",q);
replac(x,idx(q[0]));
}
}
return 0;
}

本文介绍了一种使用Splay树解决字符串操作问题的方法,包括插入字符、替换字符及查询最长公共前缀。文章详细讲解了Splay树如何维护区间信息、更新hash值,并通过实例展示了二分查找公共前缀长度的技巧。
679

被折叠的 条评论
为什么被折叠?



