本文版權歸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 }