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++;
        }
    }
}

 

posted @ 2019-08-10 14:49  SevenDawns  阅读(181)  评论(0编辑  收藏  举报
浏览器标题切换
浏览器标题切换end