[关键字]:splay hash 二分
[题目大意]:给出一个字符串,求出给定的两个后缀的的最长公共前缀。在求的过程中会有改变或在某个位置添加字符的操作。
//============================================================================================
[分析]:一听最长公共前缀马上想到后缀数组,但因为是动态维护所以后缀数组也无能为力。可以把字符串想象成一个数组,于是变成了动态插入和改变一个序列,还要能快速找到两个子区间——splay。求最长公共前缀可以用二分答案加验证的方法,二分长度先提取出这个区间然后判断这个区间的根节点的hash值(实际就是这棵子树的hash)是否和另一个相同。hash值得判断可以用进制法:hash=ord(s[1])*270+ord(s[2])*271+……+ord(s[n])*27n-1,在旋转时更新hash值为:hash[lc]+s[v]*27^(size[lc]+1)+hash[rc]*27^(size[lc]+2)。插入操作和一般splay插入无异修改只要修改掉这个点的值和hash值就行了。
[代码]:
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<algorithm> using namespace std; const int MAXL=100015; const int MOD=9875321; struct node { node *c[2],*f; int dat,size,hash; }*root,SC[MAXL],*null; char s[MAXL]; int m,SS; int d[MAXL]; void Debug(node *v) { if (v==null) return; Debug(v->c[0]); printf("%d ",v->dat); Debug(v->c[1]); } node *New(int d,int h,node *fa) { node *e=SC+ ++SS; e->size=1,e->f=fa; e->c[1]=e->c[0]=null; e->dat=d,e->hash=h; return e; } void Update(node *v) { if (v==null) return; v->size=v->c[0]->size+v->c[1]->size+1; v->hash=(long long)(v->c[0]->hash+(long long)v->dat*d[(v->c[0]->size+1)]+(long long)v->c[1]->hash*d[(v->c[0]->size+2)])%MOD; } void Prepare() { SS=-1; null=NULL; null=New(0,0,NULL); null->size=0; root=New(0,0,null); root->c[1]=New(0,0,root); d[1]=1; for (int i=2;i<=100010;++i) d[i]=(d[i-1]*27)%MOD; Update(root); } void Rotate(node *x,int o) { node *y=x->f; y->c[o]=x->c[!o]; y->c[o]->f=y; x->f=y->f; if (y->f->c[0]==y) y->f->c[0]=x; else y->f->c[1]=x; x->c[!o]=y; y->f=x; if (y==root) root=x; Update(y); } void Splay(node *x,node *fa) { while (x->f!=fa) if (x->f->f==fa) if (x->f->c[0]==x) Rotate(x,0); else Rotate(x,1); else if (x->f->f->c[0]==x->f) if (x->f->c[0]==x) Rotate(x->f,0),Rotate(x,0); else Rotate(x,1),Rotate(x,0); else if (x->f->c[1]==x) Rotate(x->f,1),Rotate(x,1); else Rotate(x,0),Rotate(x,1); Update(x); } void Select(int k,node *fa) { node *t=root; while (1) { if (k==t->c[0]->size+1) break; if (k<t->c[0]->size+1) t=t->c[0]; if (k>t->c[0]->size+1) k-=t->c[0]->size+1,t=t->c[1]; } Splay(t,fa); } int Hash(int x,int y) { Select(x,null),Select(y+2,root); return root->c[1]->c[0]->hash; } void Ask(int x,int y) { int ans=0,n=root->size-2; for (int i=1<<18;i;i>>=1) if (x+i-1<=n && y+i-1<=n && Hash(x,x+i-1)==Hash(y,y+i-1)) ans+=i,x+=i,y+=i; printf("%d\n",ans); } void Ins(int x,char ch) { Select(x+1,null),Select(x+2,root); root->c[1]->c[0]=New(ch-'a',ch-'a',root->c[1]); Splay(root->c[1]->c[0],null); //Debug(root),printf("\n"); } void Rep(int x,char ch) { Select(x,null),Select(x+2,root); node *v=root->c[1]->c[0]; v->dat=ch-'a'; v->hash=(long long)(v->c[0]->hash+(long long)v->dat*d[(v->c[0]->size+1)]+(long long)v->c[1]->hash*d[(v->c[0]->size+2)])%MOD; //Debug(root),printf("\n"); } void Init() { node *z,*t; Prepare(); scanf("%s",s); int n=strlen(s); z=t=New(s[0]-'a',s[0]-'a',null); for (int i=1;i<n;++i) z=z->c[1]=New(s[i]-'a',s[i]-'a',z); root->c[1]->c[0]=t,t->f=root->c[1]; Splay(z,null); //Debug(root),printf("\n"); } void Solve() { char ch; int x,y; scanf("%d",&m),scanf("%c",&ch); for (int i=1;i<=m;++i) { scanf("%c",&ch); if (ch=='Q') scanf("%d %d",&x,&y),Ask(x,y); else if (ch=='I') scanf("%d %c",&x,&ch),Ins(x,ch); else scanf("%d %c",&x,&ch),Rep(x,ch); scanf("%c",&ch); } } int main() { freopen("in","r",stdin); freopen("out","w",stdout); Init(); Solve(); return 0; }