BZOJ1014 JSOI2008 火星人prefix 【非旋轉Treap】*
BZOJ1014 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個數據,沒有插入操作。
我們發現有修改和新增字元的操作,就想到了平衡樹
又因為我比較喜歡非旋轉Treap
然後就寫了一波
注意這裡的Treap不能按照字元的大小來建樹,我們應該根據字串中字元的先後順序來建樹,新增字元就直接在第k個位置後面新增就好了,修改比較常規,直接先分裂再合併就行了
現在我們考慮怎麼求字串的兩個字尾的最長公共字首,首先如果沒有修改和新增操作我們肯定會選擇字尾陣列,但現在我們有了修改和新增操作,所以我們需要換一種方式思考。在一般情況下,字串的hash值是否相同可以判斷字串是否相同,所以我們可以用hash,但是如果這樣暴力判斷,時間效率依舊不優秀,達到,那麼我們可以發現這個hash段的長度是具有二分性的,所以我們就可以用二分來查詢,就可以將時間優化成
那麼我們怎麼維護hash呢,首先,在Treap上進行向上更新操作的時候可以通過合併左右區間hash值來維護,那麼我們就需要與處理base偏移量(pow陣列),然後通過
來計算當前節點為根的子樹hash值,其中val是當前節點所代表的字元的編號(字元-‘a’+1)
然後我們在二分的時候直接剝離出從x、y開始長度為len的子樹就好了
#include<bits/stdc++.h>
using namespace std;
#define N 1000010
#define Base 37
typedef pair<int,int> pi;
struct Treap{
int tot,root,n;
int siz[N],key[N],ls[N],rs[N],val[N];
unsigned int hash[N],pow[N];
void init(){
tot=root=0;
pow[0]=1;
for(int i=1;i<=N;i++)
pow[i]=pow[i-1]*Base;
}
int new_treap_point(int vl){
int newt=++tot;
siz[newt]=1;
hash[newt]=val[newt]=vl;
key[newt]=rand();
ls[newt]=rs[newt]=0;
return newt;
}
void update(int t){
siz[t]=siz[ls[t]]+siz[rs[t]]+1;
hash[t]=hash[ls[t]]+pow[siz[ls[t]]]*val[t]+pow[siz[ls[t]]+1]*hash[rs[t]];
}
int merge(int a,int b){
if(!a)return b;
if(!b)return a;
if(key[a]<key[b]){
rs[a]=merge(rs[a],b);
update(a);return a;
}else{
ls[b]=merge(a,ls[b]);
update(b);return b;
}
}
pi split(int t,int k){
if(!t)return pi(0,0);
pi ans;
if(siz[ls[t]]>=k){
pi tmp=split(ls[t],k);
ls[t]=tmp.second;
update(t);
ans.first=tmp.first;
ans.second=t;
}else{
pi tmp=split(rs[t],k-siz[ls[t]]-1);
rs[t]=tmp.first;
update(t);
ans.first=t;
ans.second=tmp.second;
}
return ans;
}
void insert(int k,int vl){
pi x=split(root,k);
int t=new_treap_point(vl);
root=merge(merge(x.first,t),x.second);
}
void erase(int k){
pi x=split(root,k);
root=merge(split(x.first,k-1).first,x.second);
}
void modify(int k,int vl){
erase(k);
insert(k-1,vl);
}
int queryhash(int t,int len){
if(t+len-1>siz[root])return -1;
pi x=split(root,t-1);
pi y=split(x.second,len);
int ans=hash[y.first];
root=merge(x.first,merge(y.first,y.second));
return ans;
}
int query(int x,int y){
int l=0,r=N,ans=0;
while(l<=r){
int mid=(l+r)>>1;
int px=queryhash(x,mid),py=queryhash(y,mid);
if(px==-1||py==-1){r=mid-1;continue;}
if(px==py)ans=mid,l=mid+1;
else r=mid-1;
}
return ans;
}
}treap;
int n,m;
char s[N],c[5];
int main(){
srand(time(0));
treap.init();
scanf("%s",s+1);
n=strlen(s+1);
treap.n=n;
for(int i=1;i<=n;i++)
treap.root=treap.merge(treap.root,treap.new_treap_point(s[i]-'a'+1));
scanf("%d",&m);
while(m--){
scanf("%s",c);
if(c[0]=='Q'){
int x,y;scanf("%d%d",&x,&y);
printf("%d\n",treap.query(x,y));
}else if(c[0]=='I'){
int x;scanf("%d%s",&x,c);
treap.insert(x,c[0]-'a'+1);
}else{
int x;scanf("%d%s",&x,c);
treap.modify(x,c[0]-'a'+1);
}
}
return 0;
}