[JSOI2008]火星人prefix

Time Limit: 10 Sec  Memory Limit: 162 MB
Submit: 8322  Solved: 2645
[Submit][Status][Discuss]

Description

  火星人最近研究了一种操作:求一个字串两个后缀的公共前缀。比方说,有这样一个字符串: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函数的值。

Input

  第一行给出初始的字符串。第二行是一个非负整数M,表示操作的个数。接下来的M行,每行描述一个操作。操
作有3种,如下所示
1、询问。语法:Qxy,x,y均为正整数。功能:计算LCQ(x,y)限制:1<=x,y<=当前字符串长度。
2、修改。语法:Rxd,x是正整数,d是字符。功能:将字符串中第x个数修改为字符d。限制:x不超过当前字
符串长度。
3、插入:语法:Ixd,x是非负整数,d是字符。功能:在字符串第x个字符之后插入字符d,如果x=0,则在字
符串开头插入。限制:x不超过当前字符串长度

Output

  对于输入文件中每一个询问操作,你都应该输出对应的答案。一个答案一行。

Sample Input

madamimadam
7
Q 1 7
Q 4 8
Q 10 11
R 3 a
Q 1 7
 

I 10 a
Q 2 11

Sample Output

5
1
0
2
1

HINT

 

1、所有字符串自始至终都只有小写字母构成。

2、M<=150,000

3、字符串长度L自始至终都满足L<=100,000

4、询问操作的个数不超过10,000个。

对于第1,2个数据,字符串长度自始至终都不超过1,000

对于第3,4,5个数据,没有插入操作。

 

题解,插入,删除这些操作应该是平衡树的基本操作,如何求最长公共前缀呢,就hash+二分判断即可。

  1 #include<cstring>
  2 #include<cmath>
  3 #include<iostream>
  4 #include<algorithm>
  5 #include<cstdio>
  6 
  7 #define mod 9875321
  8 #define ll long long
  9 #define N 150007
 10 using namespace std;
 11 inline int read()
 12 {
 13     int x=0,f=1;char ch=getchar();
 14     while(ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
 15     while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
 16     return x*f;
 17 }
 18 
 19 int n,m,rt,sz;
 20 int a[N];
 21 int c[N][2],fa[N],siz[N],val[N];
 22 int v[N],h[N],p[N],id[N];
 23 char ch[N];
 24 
 25 void update(int k)
 26 {
 27     int l=c[k][0],r=c[k][1];
 28     siz[k]=siz[l]+siz[r]+1;
 29     h[k]=h[l]+(ll)v[k]*p[siz[l]]%mod+(ll)p[siz[l]+1]*h[r]%mod;
 30     h[k]%=mod; //h表示这棵子树的hash值 
 31 }
 32 void rotate(int x,int &k)
 33 {
 34     int y=fa[x],z=fa[y],l,r;
 35     if (c[y][0]==x)l=0;else l=1;r=l^1;
 36     if (y==k) k=x;
 37     else
 38     {
 39         if (c[z][0]==y) c[z][0]=x;
 40         else c[z][1]=x;
 41     }
 42     fa[x]=z,fa[y]=x,fa[c[x][r]]=y;
 43     c[y][l]=c[x][r],c[x][r]=y;
 44     update(y),update(x);
 45 }
 46 void splay(int x,int &p)
 47 {
 48     while(x!=p)
 49     {
 50         int y=fa[x],z=fa[y];
 51         if (y!=p)
 52         {
 53             if (c[y][0]==x^c[z][0]==y) rotate(x,p);
 54             else rotate(y,p);
 55         }
 56         rotate(x,p);
 57     }
 58 }
 59 int find(int k,int rk)
 60 {
 61     int l=c[k][0],r=c[k][1];
 62     if(siz[l]+1==rk)return k;
 63     else if(siz[l]>=rk)return find(l,rk);
 64     else return find(r,rk-siz[l]-1);
 65 }
 66 void ins(int k,int val)
 67 {
 68     int x=find(rt,k+1),y=find(rt,k+2);
 69     splay(x,rt);splay(y,c[x][1]);
 70     int z=++sz;c[y][0]=z;fa[z]=y;v[z]=val;
 71     update(z);update(y);update(x);
 72 }
 73 int query(int k,int val)
 74 {
 75     int x=find(rt,k),y=find(rt,k+val+1);
 76     splay(x,rt);splay(y,c[x][1]);
 77     int z=c[y][0];
 78     return h[z];//应该比较好理解的吧,旋转出来,把那一段旋转出来,最后那个点就是代表这一段的hash值。 
 79 }
 80 int solve(int x,int y)
 81 {
 82     int l=1,r=min(sz-x,sz-y)-1,ans=0;//去掉一个虚点。 
 83     while(l<=r)
 84     {
 85         int mid=(l+r)>>1;
 86         if(query(x,mid)==query(y,mid))l=mid+1,ans=mid;
 87         else r=mid-1;
 88     }
 89     return ans;
 90 }
 91 void build(int l,int r,int f)
 92 {
 93     if(l>r)return;
 94     int now=id[l],last=id[f];
 95     if(l==r)
 96     {
 97         v[now]=h[now]=ch[l]-'a'+1;
 98         fa[now]=last;siz[now]=1;
 99         if(l<f)c[last][0]=now;
100         else c[last][1]=now;
101         return;
102     }
103     int mid=(l+r)>>1;now=id[mid];
104     build(l,mid-1,mid);build(mid+1,r,mid);
105     v[now]=ch[mid]-'a'+1;fa[now]=last;update(now);
106     if(mid<f)c[last][0]=now;
107     else c[last][1]=now;
108 }
109 void init()
110 {
111     scanf("%s",ch+2);
112     n=strlen(ch+2);
113     p[0]=1;
114     for (int i=1;i<N;i++)
115         p[i]=p[i-1]*27%mod;
116     for (int i=1;i<=n+2;i++)
117         id[i]=i;
118     build(1,n+2,0);//这类问题不要忘了放开头,结尾两个哨兵。 
119     sz=n+2,rt=(n+3)>>1;
120 }
121 int main()
122 {
123     init(),m=read();
124     while(m--)
125     {
126         char s[2];scanf("%s",s);
127         if (s[0]=='Q')
128         {
129             int x=read(),y=read();
130             printf("%d\n",solve(x,y));
131         }
132         else if (s[0]=='R')
133         {
134             int x=read();char zhi[2];scanf("%s",zhi);
135             x=find(rt,x+1);splay(x,rt);
136             v[rt]=zhi[0]-'a'+1;
137             update(rt);//修改是更加简单的。 
138         }
139         else 
140         {
141             int x=read();char zhi[2];scanf("%s",zhi);
142             ins(x,zhi[0]-'a'+1);
143         }
144     }
145 }