BZOJ 1014 [JSOI2008]火星人prefix
字符串Hash+二分+平衡树(fhq_treap)
如果没有插入和改变字符的操作,字符串Hash是可以处理出LCP的值的
就是先处理出原字符串的前缀$hash$值,然后对于两个开始的位置二分枚举LCP的长度
然后就是利用前缀和处理出$l$,$r$区间的$hash$值,进行比较即可
但是本题有插入和改变字符的操作
那么就需要用平衡树维护字符串的$hash$值
整个题中要用到平衡树的就是要查找某一区间的$hash$值
那么以下标建立一棵平衡树,对于每个节点记录该节点和其子树的hash值
那么一个节点的$hash$直接就是
$hash_{rson}+base^{size[rson]}*ch+base^{size[rson]+1}*hash_{lson}$
此处用的是fhq_treap维护整个字符串的$hash$值
#include <bits/stdc++.h> #define base 131 using namespace std; const int MAXN=101000; int m,root,tot,len; char g[MAXN]; int z[MAXN]; struct node { int ha; char c; int son[2],si,key; }sh[MAXN]; int newnode(char ch) { tot++; sh[tot].c=ch; sh[tot].ha=(int)ch; sh[tot].si=1; sh[tot].key=rand(); return tot; } void pushup(int x) { sh[x].si=sh[sh[x].son[0]].si+sh[sh[x].son[1]].si+1; sh[x].ha=sh[sh[x].son[1]].ha+z[sh[sh[x].son[1]].si]*(int)sh[x].c+z[sh[sh[x].son[1]].si+1]*sh[sh[x].son[0]].ha;//维护当前节点的hash } int build(int ll,int rr) { int mid,cur; mid=(ll+rr)>>1; cur=newnode(g[mid]); if (ll==rr) { pushup(cur); return cur; } if (ll<=mid-1) sh[cur].son[0]=build(ll,mid-1); if (mid+1<=rr) sh[cur].son[1]=build(mid+1,rr); pushup(cur); return cur; } void split(int now,int k,int &x,int &y)//fhq_treap的基本操作,此处以第k个划分 { if (now==0) { x=0; y=0; return; } if (sh[sh[now].son[0]].si>=k) { y=now; split(sh[now].son[0],k,x,sh[now].son[0]); } else { x=now; split(sh[now].son[1],k-1-sh[sh[now].son[0]].si,sh[now].son[1],y); } pushup(now); } int merge(int x,int y) { if (x==0) return y; if (y==0) return x; if (sh[x].key<=sh[y].key) { sh[x].son[1]=merge(sh[x].son[1],y); pushup(x); return x; } else { sh[y].son[0]=merge(x,sh[y].son[0]); pushup(y); return y; } } void insert(int k,char ch) { int a,b; split(root,k,a,b); root=merge(merge(a,newnode(ch)),b); } void del(int k) { int a,b,c; split(root,k,a,b); split(a,k-1,a,c); c=merge(sh[c].son[0],sh[c].son[1]); root=merge(merge(a,b),c); } void change(int k,char ch) { int a,b,c; split(root,k-1,a,b); split(b,1,b,c); sh[b].c=ch; sh[b].ha=(int)ch; root=merge(a,merge(b,c)); } int get(int l,int r)//求出某一区间的hash值 { int a,b,c; split(root,l-1,a,b); split(b,r-l+1,b,c); int h; h=sh[b].ha; b=merge(b,c); root=merge(a,b); return h; } int query(int x,int y) { if (get(x,x)!=get(y,y)) return 0; int l,r; l=0;r=min(len-x+1,len-y+1); while (l<r)//二分查找答案 { int mid; mid=l+((r-l+1)>>1); if (get(x,x+mid-1)==get(y,y+mid-1)) l=mid; else r=mid-1; } return l; } int main() { srand(time(0)); scanf("%s",g+1); len=strlen(g+1); z[0]=1; for (int i=1;i<=100000;i++) z[i]=z[i-1]*base; root=build(1,len); scanf("%d",&m); for (int i=1;i<=m;i++) { char op[3]; scanf("%s",op); if (op[0]=='Q') { int x,y; scanf("%d%d",&x,&y); printf("%d\n",query(x,y)); } if (op[0]=='R') { int x; char d[5]; scanf("%d%s",&x,d); change(x,d[0]); } if (op[0]=='I') { int x; char d[5]; scanf("%d%s",&x,d); insert(x,d[0]); len++; } } }