【题解】
动态的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; }