CQOI2010 火星人题解
火星人
【问题描述】:
火星人最近研究了一种操作:求一个字串两个后缀的公共前缀。比方说,有这样一个字
符串:madamimadam,我们将这个字符串的各个字符予以标号:
序号 1 2 3 4 5 6 7 8 9 10 11
字符 m a d a m i m a d a m
现在,火星人定义了一个函数LCQ(x, y),表示:该字符串中第x个字符开始的字串,
与该字符串中第y个字符开始的字串,两个字串的公共前缀的长度。 比方说,LCQ(1, 7) = 5,
LCQ(2, 10) = 1, LCQ(4, 7) = 0
在研究LCQ函数的过程中,火星人发现了这样的一个关联:如果把该字符串的所有后
缀排好序,就可以很快地求出LCQ函数的值;同样,如果求出了LCQ函数的值,也可以很
快地将该字符串的后缀排好序。
尽管火星人聪明地找到了求取LCQ函数的快速算法,但不甘心认输的地球人又给火星
人出了个难题:在求取LCQ函数的同时,还可以改变字符串本身。 具体地说,可以更改字
符串中某一个字符的值,也可以在字符串中的某一个位置插入一个字符。地球人想考验一下,
在如此复杂的问题中,火星人是否还能够做到很快地求取LCQ函数的值。
【输入文件】
第一行给出初始的字符串。
第二行是一个非负整数M,表示操作的个数。
接下来的M行,每行描述一个操作。操作有3种,如下所示:
1.询 问。
语法:Q x y,x, y均为正整数。
功能:计算LCQ(x, y)
限制:1 <= x, y <= 当前字符串长度。
2 .修 改。
语法:R x d,x是正整数,d是字符。
功能:将字符串中第x个数修改为字符d。
限制:x不超过当前字符串长度。
3.插 入:
语法:I x d,x是非负整数,d是字符。
功能:在字符串第x个字符之后插入字符d,如果x = 0,则在字符串开头插入。
限制:x不超过当前字符串长度。
因为本弱不会后缀数组(后来知道这个题还不能用后缀数组做),就想用字符串hash来解决这个问题。
因为要维护插入和删除操作,所以我们维护一棵Splay,每个节点保存的是以当前节点为根的字符串的hash值。
寻找lcq的时候,直接二分答案就行了。
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
const int maxn=1110000;
const int c=13331;
struct Node{
char a;
int hash,size;
Node *ch[2],*fa;
}Edge[maxn];
Node *Cnt1=&Edge[0];
int m,Size,Hp[maxn];
Node *New_Node(char a){
Node *p=++Cnt1;
p->a=a;
p->hash=Hp[0]*a;
p->ch[0]=p->ch[1]=p->fa=NULL;
p->size=1;
return p;
}
Node *Root;
char Data[maxn];
void Update(Node *x){
if(x==NULL) return ;
x->size=(x->ch[0]!=NULL?x->ch[0]->size:0)+(x->ch[1]!=NULL?x->ch[1]->size:0)+1;
int S=x->size;
//int L=(x->ch[0]!=NULL?x->ch[0]->size:0);
int s0=x->ch[0]!=NULL?x->ch[0]->hash:0,s1=x->ch[1]!=NULL?x->ch[1]->hash:0;
int len0=x->ch[0]!=NULL?x->ch[0]->size:0,len1=(x->ch[1]!=NULL?x->ch[1]->size:0);
x->hash=s0*Hp[S-len0]+x->a*Hp[len1]+s1;
}
void Rotate(Node *x,int d){
Node *y=x->fa;
x->fa->ch[d^1]=x->ch[d];
x->fa=y->fa;
if(x->ch[d]!=NULL) x->ch[d]->fa=y;
if(y->fa!=NULL) y->fa->ch[y==y->fa->ch[1]]=x;
y->fa=x;
x->ch[d]=y;
if(y==Root) Root=x;
Update(y);
Update(x);
}
void Splay(Node *x,Node *rt){
if(x==NULL) return ;
while(x->fa!=rt){
Node *y=x->fa;
Node *z=y->fa;
if(z==rt){
if(x==y->ch[0]) Rotate(x,1);
else Rotate(x,0);
}
else{
if(z->ch[0]==y){
if(x==y->ch[0]){ Rotate(y,1); Rotate(x,1); }
else { Rotate(x,0); Rotate(x,1); }
}
else{
if(y->ch[1]==x){ Rotate(y,0); Rotate(x,0); }
else{
Rotate(x,1);
Rotate(x,0);
}
}
}
}
Update(x);
}
Node *Build(int L,int R,Node *&Fa){
if(L>R) return NULL;
int Mid=(L+R)>>1;
Node *o=New_Node(Data[Mid]);
o->ch[0]=Build(L,Mid-1,o);
o->ch[1]=Build(Mid+1,R,o);
o->fa=Fa;
Update(o);
return o;
}
Node *Kth(Node *o,int k){
if(o==NULL) return o;
if((o->ch[0]!=NULL?o->ch[0]->size:0)>=k)
return Kth(o->ch[0],k);
k=k-(o->ch[0]!=NULL?o->ch[0]->size:0);
if(k<=1) return o;
return Kth(o->ch[1],k-1);
}
void _Insert(int x,char a){
Node *K=Kth(Root,x);
Splay(K,NULL);
Node *K1=Kth(Root,x+1);
Splay(K1,Root);
Node *o=New_Node(a);
if(Root->ch[1]!=NULL){
Root->ch[1]->ch[0]=o;
o->fa=Root->ch[1];
Update(Root->ch[1]);
Update(Root);
}
else{
Root->ch[1]=o;
o->fa=Root;
Update(o);
Update(Root);
}
}
void Modify(int x,char d){
Node *K=Kth(Root,x);
Splay(K,NULL);
Root->a=d;
Update(Root);
}
int RK(int L,int R){
if(L>1&&R<Size){
Node *L1=Kth(Root,L-1);
Splay(L1,NULL);
Node *R1=Kth(Root,R+1);
Splay(R1,Root);
return Root->ch[1]->ch[0]->hash;
}
if(L==1&&R<Size){
Node *R1=Kth(Root,R+1);
Splay(R1,NULL);
return Root->ch[0]->hash;
}
if(L>1&&R==Size){
Node *L1=Kth(Root,L-1);
Splay(L1,NULL);
return Root->ch[1]->hash;
}
if(L==1&&R==Size){
return Root->hash;
}
return 0;
}
bool Check(int Ans,int x,int y){
int wdy1=RK(x,x+Ans-1);
int wdy2=RK(y,y+Ans-1);
if(wdy1==wdy2)
return 1;
else
return 0;
}
void Query(int x,int y){
int r=Size-y+1,l=0;
while(l<r){
int mid=(l+r+1)>>1;
if(Check(mid,x,y))
l=mid;
else
r=mid-1;
}
printf("%d\n",r);
}
int main()
{
//freopen("wdy.in","r",stdin);
scanf("%s",&Data[1]);
Size=strlen(Data+1);
Hp[0]=1;
for(int i=1;i<maxn;i++)
Hp[i]=Hp[i-1]*c;
Root=Build(1,Size,Root);
scanf("%d",&m);
for(int i=1;i<=m;i++){
char Cmd[10],d;
int x,y;
scanf("%s",&Cmd[1]);
if(Cmd[1]=='Q'){
scanf("%d%d",&x,&y);
//printf("%d:",i);
Query(x,y);
}
if(Cmd[1]=='R'){
scanf("%d %c",&x,&d);
Modify(x,d);
}
if(Cmd[1]=='I'){
scanf("%d %c",&x,&d);
_Insert(x,d);
Size++;
}
//printf("%d: \n",i);
}
return 0;
}