BZOJ_1014_[JSOI2008]_火星人prefix_(Splay+LCP_Hash+二分)
Posted 晴歌。
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了BZOJ_1014_[JSOI2008]_火星人prefix_(Splay+LCP_Hash+二分)相关的知识,希望对你有一定的参考价值。
描述
http://www.lydsy.com/JudgeOnline/problem.php?id=1014
给出一个字符串,有修改,插入,以及询问LCP(i,j)的操作.
分析
LCP在白书上面有介绍,\\(LCP(i,j)\\)表示以第\\(i\\)位和以第\\(j\\)位开头的后缀的最长公共前缀.
先考虑没有插入和修改操作的问题.我们可以用基于Hash的LCP算法.
我们给每一个后缀一个Hash值.其中以第\\(i\\)为开头的后缀的Hash值为\\(H[i]=H[i+1]x+s[i]\\).
其中\\(x\\)是随便一个什么数.例如:
\\(H[4]=s[4]\\)
\\(H[3]=s[4]x+s[3]\\)
\\(H[2]=s[4]x^2+s[3]x+s[2]\\)
\\(H[1]=s[4]x^3+s[3]x^2+s[2]x+s[1]\\)
一般地有:
$$H[i]=s[n]x^{n-i}+s[n-1]x^{n-1-i}+...+s[i+1]x+s[i]$$
对于字符串\\(s[i]~s[i+L-1]\\)(长度为L),定义它的Hash值为:
$$Hash(i,L)=H[i]-H[i+L]x^L$$
其实就是相当于把以\\(i\\)开头的后缀的Hash值有关\\(i+L\\)以及后面的部分都砍掉.
当然这个Hash值可以定义为前缀的形式,和后缀的没有区别.
但是注意,并不是字符串不同,Hash值一定不同,只是相同的概率极低,基本可以无视.
至于计算,我们采用unsigned long long ,这样自然溢出相当于对\\(2^{64}\\)取模.
这样我们就可以判断字串\\((i,L)\\)与\\((j,L)\\)是否相等,二分\\(L\\)的值,取最大可行解即可.
至于修改和插入操作?平衡树来解决咯.平衡树上每个结点的Hash值代表的是以该结点为根的子树代表的字符串的Hash值.
p.s.
1.复习了下Splay,好不熟练啊,还要多练习,不然药丸...
2.调了好久发现是字符串读入的问题...(拍脸)
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 const int maxn=1e5+5; 5 typedef unsigned long long ull; 6 int n,m; 7 ull p[maxn]; 8 char s[maxn]; 9 struct Splay{ 10 struct node{ 11 node* c[2],* f; 12 int v,s; ull h; 13 node(int v,node* t):v(v){ s=1;h=v;f=c[0]=c[1]=t; } 14 bool d(){ return f->c[1]==this; } 15 void setc(node* x,bool d){ c[d]=x; x->f=this; } 16 void push_up(){ 17 s=c[0]->s+c[1]->s+1; 18 h=c[0]->h+(ull)v*p[c[0]->s]+c[1]->h*p[c[0]->s+1]; 19 } 20 }* root,* null; 21 Splay(){ 22 null=new node(0,0);null->s=0; 23 root=new node(0,null); root->setc(new node(0,null),1); 24 } 25 void rot(node* x){ 26 node* f=x->f; bool d=x->d(); 27 f->f->setc(x,f->d()); 28 f->setc(x->c[!d],d); 29 x->setc(f,!d); 30 f->push_up(); 31 if(f==root) root=x; 32 } 33 void splay(node* x,node *f){ 34 while(x->f!=f) 35 if(x->f->f==f) rot(x); 36 else x->d()==x->f->d()?(rot(x->f),rot(x)):(rot(x),rot(x)); 37 x->push_up(); 38 } 39 node* kth(int k){ 40 for(node* t=root;t!=null;){ 41 int s=t->c[0]->s; 42 if(k==s) return t; 43 if(k>s) t=t->c[1], k-=s+1; 44 else t=t->c[0]; 45 } 46 } 47 node* get_range(int l,int r){ 48 splay(kth(l-1),null); 49 splay(kth(r+1),root); 50 return root->c[1]->c[0]; 51 } 52 void ins(int v,int pos){ 53 node *f=get_range(pos,pos); 54 f->setc(new node(v,null),1); splay(f->c[1],null); 55 } 56 void chg(int v,int pos){ 57 node *x=get_range(pos,pos); 58 x->v=v; splay(x,null); 59 } 60 int hash(int l,int r){ return get_range(l,r)->h; } 61 node* build(int l,int r){ 62 if(l>r) return null; 63 int m=l+(r-l)/2; 64 node *t=new node(s[m]-\'a\'+1,null); 65 t->setc(build(l,m-1),0); 66 t->setc(build(m+1,r),1); 67 t->push_up(); 68 return t; 69 } 70 }T; 71 inline int read(int &x){ x=0;int k=1;char c;for(c=getchar();c<\'0\'||c>\'9\';c=getchar())if(c==\'-\')k=-1;for(;c>=\'0\'&&c<=\'9\';c=getchar())x=x*10+c-\'0\';return x*=k; } 72 inline char read(char &c){ for(c=getchar();(c<\'a\'||c>\'z\')&&(c<\'A\'||c>\'Z\');c=getchar());return c; } 73 int bsearch(int x,int y){ 74 int s=T.root->s-2; 75 int l=0,r=min(s-x,s-y)+1,mid; 76 while(l<r){ 77 mid=l+(r-l+1)/2; 78 if(T.hash(x,x+mid-1)==T.hash(y,y+mid-1)) l=mid; 79 else r=mid-1; 80 } 81 return l; 82 } 83 void init(){ 84 scanf("%s",s+1); n=strlen(s+1); read(m); 85 p[0]=1; 86 for(int i=1;i<maxn;i++) p[i]=p[i-1]*(ull)27; 87 T.root->c[1]->setc(T.build(1,n),0); T.root->c[1]->push_up(); T.root->push_up(); 88 } 89 int main(){ 90 init(); 91 while(m--){ 92 char c; int x,y; 93 read(c); read(x); 94 if(c==\'Q\'){ read(y); printf("%d\\n",bsearch(x,y)); } 95 else if(c==\'R\') T.chg(read(c)-\'a\'+1,x); 96 else T.ins(read(c)-\'a\'+1,x); 97 } 98 return 0; 99 }
以上是关于BZOJ_1014_[JSOI2008]_火星人prefix_(Splay+LCP_Hash+二分)的主要内容,如果未能解决你的问题,请参考以下文章
BZOJ 1014 [JSOI2008]火星人prefix (Splay + Hash + 二分)