首页 > 代码库 > bzoj 1014: [JSOI2008]火星人prefix

bzoj 1014: [JSOI2008]火星人prefix

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个数据,没有插入操作。

Source

 

 

 

今天终于把这个名为"火星人”的坑给填了。。。

这道题曾经对刚打完营业额统计的我产生了巨大的心理阴影。。。

直到昨天晚自习无聊看刘汝佳白书,陡然发现有一个名叫魔法珠宝的题勾起了我童年的回忆。。。

对于字符串的多次修改,后缀数组是不能胜任的(蒟蒻并不会),看到插入,修改。。splay就能排上用场了。。。

求LCP的话用二分+哈希可以在logn的时间判断,就是二分一个长度用哈希值判断两个串是否相等。。。

不得不说哈希真的是解决字符串问题的万能方法。。。O(1)判断啊,开玩笑。。。。

splay的作用就是用来维护这个字符串的。。。其中每个节点相当与掌握了他所管辖区间的一段的哈希值。。。。

update的时候再由左右儿子的哈希值重新计算,这就需要再维护一个子树大小了。。。(算哈希用)。。。。

就像这样:hsh[x]=hsh[left[x]]+v[x]*pre[size[left]]+hsh[r]*pre[size[left]+1]。。。。。

每次询问相当于是要提取一段的哈希值。。。

对于询问,对于需要的一个子串首先找到子串的首字母,把它伸展到根,然后找到子串的尾字母的下一个字母,把它伸展到根的右儿子,那么根的右儿子的左儿子的哈希值就是答案。

对于插入操作:

把插入位置splay到根,下一位splay到根的右子树,然后直接往根的右子树的左子树上插就可以了,直接在根结点上修改再update()。

修改操作就没什么好说的了。。。直接splay到根,修改再update();

不得不说这个题真的有点码,关键是难调。。。splay天旋地转调个鬼。。。

Orz 这是jesseliu612大佬的splay入门题。。。。

今天长见识了,原来是不能定hash这个数组的。。。CE。。。

貌似跑得贼慢。。。9600ms踩线过

  1 // MADE BY QT666
  2 #include<cstdio>
  3 #include<algorithm>
  4 #include<cmath>
  5 #include<iostream>
  6 #include<queue>
  7 #include<set>
  8 #include<cstdlib>
  9 #include<cstring>
 10 #include<string>
 11 #include<ctime>
 12 #define lson num<<1
 13 #define rson num<<1|1
 14 #define int long long
 15 using namespace std;
 16 typedef long long ll;
 17 const int N=150005;
 18 const int mod=23333333;
 19 int size[N],v[N],hsh[N],pre[N];
 20 int c[N][2],fa[N];
 21 int n,m,rt,sz;
 22 char ch[N];
 23 int gi()
 24 {
 25   int x=0,flag=1;
 26   char ch=getchar();
 27   while(ch<0||ch>9){if(ch==-) flag=-1;ch=getchar();}
 28   while(ch>=0&&ch<=9) x=x*10+ch-0,ch=getchar();
 29   return x*flag;
 30 }
 31 inline void update(int x)
 32 {
 33     int l=c[x][0],r=c[x][1];
 34     size[x]=size[l]+size[r]+1;
 35     hsh[x]=hsh[l]+v[x]*pre[size[l]]+pre[size[l]+1]*hsh[r];
 36 }                         
 37 inline void rotate(int x,int &k)
 38 {
 39     int y=fa[x],z=fa[y],l,r;
 40     if(c[y][0]==x)l=0;else l=1;r=l^1;
 41     if(y==k) k=x;
 42     else{if(c[z][0]==y) c[z][0]=x;else c[z][1]=x;}
 43     fa[x]=z;fa[y]=x;fa[c[x][r]]=y;
 44     c[y][l]=c[x][r],c[x][r]=y;
 45     update(y);update(x);
 46 }
 47 inline void splay(int x,int &k)
 48 {
 49   while(x!=k)
 50    {
 51       int y=fa[x],z=fa[y];
 52       if(y!=k)
 53       {
 54           if((c[y][0]==x)^(c[z][0]==y)) rotate(x,k);
 55           else rotate(y,k);
 56        }
 57       rotate(x,k);
 58    }
 59 }
 60 inline int find(int x,int y)
 61 {
 62     int l=c[x][0],r=c[x][1];
 63     if(size[l]+1==y) return x;
 64     else if(size[l]>=y) return find(l,y);
 65     else return find(r,y-size[l]-1);
 66 }
 67 inline int query(int k,int len)
 68 {
 69     int x=find(rt,k),y=find(rt,k+len+1);
 70     splay(x,rt);splay(y,c[x][1]);
 71     return hsh[c[y][0]];
 72 }
 73 inline void insert(int k,int val)
 74 {
 75     int x=find(rt,k+1),y=find(rt,k+2);
 76     splay(x,rt);splay(y,c[x][1]);
 77     int z=++sz;c[y][0]=z;fa[z]=y;v[z]=val;
 78     update(z);update(y);update(x);
 79 }
 80 inline int work(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 inline void build(int l,int r,int f)
 92 {
 93    if(l>r)return;
 94    int mid=(l+r)>>1;
 95    if(l==r)
 96     {
 97        v[l]=hsh[l]=ch[l]-a+1;size[l]=1;
 98     }
 99     else build(l,mid-1,mid),build(mid+1,r,mid);
100     v[mid]=ch[mid]-a+1;fa[mid]=f;update(mid);
101     if(mid<f)c[f][0]=mid;
102     else c[f][1]=mid;
103  }
104 main()
105 {
106     scanf("%s",ch+2);
107     n=strlen(ch+2);
108     pre[0]=1;for(int i=1;i<=N-1;i++) pre[i]=pre[i-1]*27;
109     build(1,n+2,0);sz=n+2;rt=(n+3)>>1;
110     m=gi();
111     int x,y;
112     char s[2],d[2];
113     for(int i=1;i<=m;i++)
114     {
115         scanf("%s",s+1);
116         x=gi();
117         if(s[1])
118         {
119             if(s[1]==Q) y=gi(),printf("%lld\n",work(x,y));
120             else if(s[1]==R)
121             {
122              scanf("%s",d+1);x=find(rt,x+1);splay(x,rt);
123              v[rt]=d[1]-a+1;update(rt);
124             }
125             else if(s[1]==I) {scanf("%s",d+1);insert(x,d[1]-a+1);}
126         }
127     }
128     return 0;
129 }

 

bzoj 1014: [JSOI2008]火星人prefix