BZOJ1014 [JSOI2008]火星人prefix
本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作。
本文作者:ljh2000
作者博客:http://www.cnblogs.com/ljh2000-jump/
转载请注明出处,侵权必究,保留最终解释权!
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
7
Q 1 7
Q 4 8
Q 10 11
R 3 a
Q 1 7
I 10 a
Q 2 11
Sample Output
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+二分答案+hash
解题报告:
这道题需要我们维护一个字符串,每次快速查询两个后缀的LCP,并且动态修改、往这个字符串插入元素。开始我一直在想用后缀数据结构,因为修改和插入操作不知道该怎么办,没有思路。后来发现插入和修改事实上可以用splay维护,而我的splay上的结点维护的就是这个结点控制的区间的,这一段的字符串的hash值。然后每次旋转的时候重新算一下就可以了。
根据上述所说,插入操作,我可以旋转插入位置到根,下一位到根的右子树,然后直接往右子树的左子树上插就可以了。这样做的好处就是不用一路update,直接在根结点上修改。
修改操作就更简单了,把修改的点旋转到根结点,然后修改、update即可。
我们接下来考虑查询怎么做,因为需要查询两个后缀的LCP,我们并不能直接求出值,但是判断一下还是可以的,于是想到二分答案,然后splay旋转区间,比较hash值就可以了。
这道题细节肯定没有维护数列多,不过我貌似常数写大了,虽然没TLE,但是跑的很慢我很不爽啊...于是就是一顿优化常数,比如把find改成非递归式,还有修改操作改成不旋转,找到之后一路往上update。
大概就是这样了。
ps:结果BZOJ太慢了,后来改成了自然溢才过。
1 //It is made by ljh2000 2 #include <iostream> 3 #include <cstdlib> 4 #include <cstring> 5 #include <cstdio> 6 #include <cmath> 7 #include <algorithm> 8 #include <ctime> 9 #include <vector> 10 #include <queue> 11 #include <map> 12 #include <set> 13 using namespace std; 14 typedef unsigned long long LL; 15 #define RG register 16 const int inf = (1<<30); 17 const int MAXN = 100011; 18 //const int MOD = 1000007; 19 int n,m,ans,tot,rt,ql,qr; 20 char s[MAXN],ch[12]; 21 LL mi[MAXN],hash[MAXN]; 22 int tr[MAXN][2],size[MAXN],father[MAXN]; 23 24 inline int getint() 25 { 26 RG int w=0,q=0; RG char c=getchar(); 27 while((c<'0' || c>'9') && c!='-') c=getchar(); if(c=='-') q=1,c=getchar(); 28 while (c>='0' && c<='9') w=w*10+c-'0', c=getchar(); return q ? -w : w; 29 } 30 31 inline void update(RG int x){ 32 RG int l=tr[x][0],r=tr[x][1]; size[x]=size[l]+size[r]+1; 33 hash[x]=hash[r]+(s[x]-'a'+1)*mi[size[r]]; //hash[x]%=MOD; 34 hash[x]+=hash[l]*mi[size[r]+1];// hash[x]%=MOD; 35 } 36 37 inline void rotate(RG int x,RG int &rt){ 38 RG int y=father[x],z=father[y]; 39 RG int l=(tr[y][1]==x),r=l^1; 40 if(y==rt) rt=x; else tr[z][(tr[z][1]==y)]=x; 41 father[x]=z; tr[y][l]=tr[x][r]; father[tr[x][r]]=y; 42 father[y]=x; tr[x][r]=y; 43 update(y); update(x); 44 } 45 46 inline void splay(RG int x,RG int &rt){ 47 RG int y,z; 48 while(x!=rt) { 49 y=father[x]; z=father[y]; 50 if(y!=rt) { 51 if((tr[z][0]==y) ^ (tr[y][0]==x)) rotate(x,rt); 52 else rotate(y,rt); 53 } 54 rotate(x,rt); 55 } 56 } 57 58 inline void build(RG int l,RG int r,RG int fa){ 59 if(l>r) return ; RG int mid=(l+r)>>1; 60 if(l==r) { hash[l]=s[l]-'a'+1; size[l]=1; } 61 else build(l,mid-1,mid),build(mid+1,r,mid); 62 tr[fa][mid>fa]=mid; father[mid]=fa; update(mid); 63 } 64 65 inline int rank(RG int root,RG int k){ 66 RG int l,r; 67 while(1) { 68 l=tr[root][0],r=tr[root][1]; 69 if(size[l]+1==k) return root; 70 if(size[l]>=k) root=l; else root=r,k=k-size[l]-1; 71 } 72 } 73 74 inline bool check(RG int len){ 75 RG int hash1; RG int x=rank(rt,ql); splay(x,rt); 76 x=rank(rt,ql+len+1); splay(x,tr[rt][1]); x=tr[tr[rt][1]][0]; 77 hash1=hash[x]; 78 RG int hash2; x=rank(rt,qr); splay(x,rt); 79 x=rank(rt,qr+len+1); splay(x,tr[rt][1]); x=tr[tr[rt][1]][0]; 80 hash2=hash[x]; 81 if(hash1==hash2) return true; 82 return false; 83 } 84 85 inline void query(){ 86 ql=getint(),qr=getint(); ans=0; 87 RG int l=1,r=tot-2-max(ql,qr)+1,mid; 88 while(l<=r) { 89 mid=(l+r)>>1; 90 if(check(mid)) l=mid+1,ans=mid; 91 else r=mid-1; 92 } 93 printf("%d\n",ans); 94 } 95 96 inline void insert(){ 97 RG int pos=getint(); scanf("%s",ch); s[++tot]=ch[0]; hash[tot]=ch[0]-'a'+1; 98 RG int x=rank(rt,pos+1); 99 splay(x,rt); x=rank(rt,pos+2); splay(x,tr[rt][1]); 100 x=tr[rt][1]; father[tot]=x; size[tot]=1; 101 tr[x][0]=tot; update(x); update(rt); 102 } 103 104 inline void change(){//只需要修改一条链,不用rotate 105 RG int pos=getint(); scanf("%s",ch); 106 RG int x=rank(rt,pos+1); splay(x,rt); 107 s[x]=ch[0]; update(x); 108 } 109 110 inline void work(){ 111 scanf("%s",s+2); n=strlen(s+2); m=getint(); 112 mi[0]=1; for(RG int i=1;i<=100010;i++) mi[i]=mi[i-1]*27;//,mi[i]%=MOD; 113 s[1]=s[n+2]='a'; build(1,n+2,0); tot=n+2; rt=(1+n+2)/2; 114 while(m--) { 115 scanf("%s",ch); 116 if(ch[0]=='Q') query(); 117 else if(ch[0]=='I') insert(); 118 else change(); 119 } 120 } 121 122 int main() 123 { 124 work(); 125 return 0; 126 }