首页 > 代码库 > 【bzoj1014】[JSOI2008]火星人prefix

【bzoj1014】[JSOI2008]火星人prefix

1014: [JSOI2008]火星人prefix

Time Limit: 10 Sec  Memory Limit: 162 MB
Submit: 6031  Solved: 1917
[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个数据,没有插入操作。

 

 

【题解】

用splay维护字符串,把一段子串用哈希值表示,两个长度相同的子串如果哈希值相同,那么它们就相同。

建树时每棵子树的哈希值集中在根节点上,即h[x]=(h[left[x]]+v[x]*p[size[left]]+h[r]*p[size[left]+1])%mod

查询时二分一个答案,然后验证。

那么如何找到子串的哈希值呢?

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

吐槽:这题交了很多遍,都是RE,然后我才知道BZOJ上用cin会RE......

  1 #include<iostream>  2 #include<cstdio>  3 #include<cstdlib>  4 #include<cstring>  5 #include<cmath>  6 #include<ctime>  7 #include<algorithm>  8 using namespace std;  9 #define MAXN 150010 10 #define ll long long 11 #define mod 9875321 12 int n,m,rt,sz,p[MAXN],c[MAXN][2],fa[MAXN],size[MAXN],h[MAXN],v[MAXN]; 13 char ch[MAXN]; 14 namespace INIT 15 { 16 inline int read() 17 { 18     int x=0,f=1;  char ch=getchar(); 19     while(!isdigit(ch))  {if(ch==-)  f=-1;  ch=getchar();} 20     while(isdigit(ch))  {x=x*10+ch-0;  ch=getchar();} 21     return x*f; 22 } 23 }using namespace INIT; 24 namespace SPLAY 25 { 26 void updata(int x) 27 { 28     int l=c[x][0],r=c[x][1]; 29     size[x]=size[l]+size[r]+1; 30     h[x]=h[l]+(ll)v[x]*p[size[l]]%mod+(ll)p[size[l]+1]*h[r]%mod; 31     h[x]%=mod; 32 } 33 void rotate(int x,int &k) 34 { 35     int y=fa[x],z=fa[y],l,r; 36     if(c[y][0]==x)  l=0;  else l=1;  r=l^1; 37     if(y==k)  k=x; 38     else {if(c[z][0]==y)  c[z][0]=x;  else c[z][1]=x;} 39     fa[x]=z;  fa[y]=x;  fa[c[x][r]]=y; 40     c[y][l]=c[x][r];  c[x][r]=y; 41     updata(y);  updata(x); 42 } 43 void Splay(int x,int &k) 44 { 45     while(x!=k) 46     { 47         int y=fa[x],z=fa[y]; 48         if(y!=k) 49         { 50             if((c[y][0]==x)^(c[z][0]==y))  rotate(x,k); 51             else rotate(y,k); 52         } 53         rotate(x,k); 54     } 55 } 56 void build(int k,int l,int r) 57 { 58     if(l>r)  return; 59     if(l==r) 60     { 61         v[l]=h[l]=ch[l]-a+1; 62         fa[l]=k;  size[l]=1; 63         if(l<k)  c[k][0]=l; 64         else c[k][1]=l; 65         return; 66     } 67     int mid=(l+r)/2; 68     build(mid,l,mid-1);  build(mid,mid+1,r); 69     v[mid]=ch[mid]-a+1; fa[mid]=k;  updata(mid); 70     if(mid<k)  c[k][0]=mid; 71     else c[k][1]=mid; 72 } 73 int find(int k,int rank) 74 { 75     int l=c[k][0],r=c[k][1]; 76     if(size[l]+1==rank)  return k; 77     else if(size[l]>=rank)  return find(l,rank); 78     else return find(r,rank-size[l]-1); 79 } 80 int check(int k,int val) 81 { 82     int x=find(rt,k),y=find(rt,k+val+1); 83     Splay(x,rt);  Splay(y,c[x][1]); 84     int z=c[y][0]; 85     return h[z]; 86 } 87 int ask(int x,int y) 88 { 89     int l=1,r=min(sz-x,sz-y)-1,ans=0; 90     while(l<=r) 91     { 92         int mid=(l+r)/2; 93         if(check(x,mid)==check(y,mid))   {l=mid+1;  ans=mid;} 94         else r=mid-1; 95     } 96     return ans; 97 } 98 void insert(int k,int val) 99 {100     int x=find(rt,k+1),y=find(rt,k+2);101     Splay(x,rt);  Splay(y,c[x][1]);102     int z=++sz;  c[y][0]=z;  fa[z]=y;  v[z]=val;103     updata(z);  updata(y);  updata(x);104 }105 }using namespace SPLAY;106 int main()107 {108     //freopen("cin.in","r",stdin);109     //freopen("cout.out","w",stdout);110     scanf("%s",ch+2);111     n=strlen(ch+2);   p[0]=1;112     for(int i=1;i<=MAXN;i++)  p[i]=p[i-1]*27%mod;113     build(0,1,n+2);  rt=(n+3)/2;  sz=n+2;114     m=read();   int x,y;115     char s[2],d[2];116     for(int i=1;i<=m;i++)117     {118         scanf("%s",s+1);  x=read();119         if(s[1]==Q)  {y=read();  printf("%d\n",ask(x,y));}120         if(s[1]==R)  {scanf("%s",d+1);  x=find(rt,x+1);  Splay(x,rt);  v[rt]=d[1]-a+1;  updata(rt);}121         if(s[1]==I)  {scanf("%s",d+1);  insert(x,d[1]-a+1);}122     }123     return 0;124 }

附makedata程序(博主很良心,知道你会wa):

 1 #include<iostream> 2 using namespace std; 3 char ch[5]={Q,R,I}; 4 int main() 5 { 6     freopen("cin.in","w",stdout); 7     srand(time(NULL)); 8     int n=100; 9     for(int i=1;i<=n;i++)  printf("%c",(char)(a+rand()%26));10     printf("\n");11     int m=1500;12     printf("%d\n",m);13     for(int i=1;i<=m;i++)14     {15         char f=ch[rand()%3];16         if(f==Q)  {printf("%c %d %d\n",f,rand()%n+1,rand()%n+1);}17         if(f==R)  {printf("%c %d %c\n",f,rand()%n+1,(char)(a+rand()%26));}18         if(f==I)  {printf("%c %d %c\n",f,rand()%n+1,(char)(a+rand()%26));}19     }20     return 0;21 }

 

【bzoj1014】[JSOI2008]火星人prefix