【题解】
动态的LCP问题
用 Splay 处理动态区间:
插入操作"I x d"的实现:
首先将x旋转至树根,则d应插在x的右字树中
找到 x的右子树的最左端结点(即原来的s[x+1]在树中的对应结点),将d添加为它的左孩子
用 字符串Hash 判断字符串是否相等:
o->H表示由o及其左右子树所对应字母构成的字符串的Hash值,
则 o->H = o->ch[0]->H + o->s*xp[o->ch[0]->size] + o->ch[1]->H*xp[o->ch[0]->size+1]
求一段区间[a,a+L-1]的Hash(s[a]~s[a+L-1]):
将s[a-1]旋转至根,则root->ch[1]->H为s[a]的后缀Hash值(在序列最左端添加"哨兵",防止a-1==0),记为ha1
将s[a+L-1]旋转至根,则root->ch[1]->H为s[a+L]的后缀Hash值,记为ha2
Hash(s[a]~s[a+L-1])=ha1 - ha2*(X^p)
求LCP,即"Q x y":
若s[x]==s[y],二分查找长度L,使Hash( s[x]~s[x+L-1] )==Hash( s[y]~s[y+L-1] )
注意:
1. 序列长度并非任何时候都是n,因此在预处理xp数组时,应从1循环到10^5; 二分答案时用root->s表示长度,不能随便试用n
2. 警惕手抖将ch[0]打成ch[d]等等
【代码】
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define X 23ll
typedef unsigned long long ULL;
ULL xp[100010];
char s[100010];
struct Node
{
Node* ch[2];
ULL H;
int v,s;
int cmp(int x) const
{
if( x == ch[0]->s + 1 )return -1;
if( x <= ch[0]->s ) return 0;
return 1;
}
};
Node *root,*null;
int max(int a,int b)
{
if(a>b) return a;
return b;
}
void init()
{
null=new Node();
null->ch[0]=null->ch[1]=NULL;
null->H=0;
null->v=null->s=0;
root=null;
}
void wh(Node* &o)
{
o->s = o->ch[0]->s + 1 + o->ch[1]->s;
o->H = o->ch[0]->H + (ULL)o->v*xp[o->ch[0]->s] + o->ch[1]->H*xp[o->ch[0]->s+1];
}
void build(Node* &o,int left,int right)
{
int mid=(left+right)/2;
o=new Node();
o->ch[0]=o->ch[1]=null;
o->v=s[mid];
if(left<mid) build(o->ch[0],left,mid-1);
if(right>mid) build(o->ch[1],mid+1,right);
wh(o);
}
void xz(Node* &o,int d)
{
Node* k=o->ch[d^1];
o->ch[d^1]=k->ch[d];
k->ch[d]=o;
wh(o);
wh(k);
o=k;
}
void splay(Node* &o,int k)
{
int d=o->cmp(k),d2,k2;
if(d==1) k -= o->ch[0]->s + 1;
if(d!=-1)
{
Node* p=o->ch[d];
k2=k;
d2=p->cmp(k2);
if(d2==1) k2 -= p->ch[0]->s + 1;
if(d2!=-1)
{
splay(p->ch[d2],k2);
if(d==d2) xz(o,d^1);
else xz(o->ch[d],d2^1);
}
xz(o,d^1);
}
}
int possible(int x,int y,int L)
{
ULL h1,h2;
if(L==0) return 1;
splay(root,x-1);
h1=root->ch[1]->H;
splay(root,x+L-1);
h1-=root->ch[1]->H*xp[L];
splay(root,y-1);
h2=root->ch[1]->H;
splay(root,y+L-1);
h2-=root->ch[1]->H*xp[L];
return h1==h2;
}
int cx(int x,int y)
{
int left=0,right=root->s-max(x,y)+1,mid;
while(left<right)
{
mid=(left+right+1)/2;
if(possible(x,y,mid)) left=mid;
else right=mid-1;
}
return left;
}
void tj(Node* &o,int x)
{
if(o==null)
{
o=new Node();
o->ch[0]=o->ch[1]=null;
o->v=x;
o->H=(ULL)x;
o->s=1;
return;
}
tj(o->ch[0],x);
wh(o);
}
int main()
{
char opt,d;
int n,m,i,x,y;
init();
scanf("%s%d",s,&m);
n=strlen(s);
for(i=n;i>=1;i--)
s[i]=s[i-1];
s[0]=0;
xp[0]=1LL;
for(i=1;i<=100005;i++)
xp[i]=X*xp[i-1];
build(root,0,n);
for(;m>0;m--)
{
scanf("\n%c",&opt);
if(opt=='Q')
{
scanf("%d%d",&x,&y);
printf("%d\n",cx(x+1,y+1));
}
if(opt=='R')
{
scanf("%d %c",&x,&d);
splay(root,x+1);
root->v=d;
wh(root);
}
if(opt=='I')
{
scanf("%d %c",&x,&d);
splay(root,x+1);
tj(root->ch[1],d);
wh(root);
}
}
return 0;
}