[BZOJ1014] [JSOI2008]火星人prefix

这道题需要用到一个叫Rabin-Karp的算法 自行百度或者看算导 不作解释 

可以看的出来这是一个字符串匹配的题目 单由于一些特殊的插入操作 使得其本身变得难以维护 所以引入splay进行维护


对于树中的每一个节点 保存以其为根的子树的RK值(要包括左右子树 自己放在左右子树中间) 这样我们就可以快速的找到一个字符串的RK值


对于RK值的维护 可在splay旋转过程中利用左右儿子的RK值 直接进行计算 算式如下

 RK = ( ((LL)ch[0]->RK * base[ch[1]->sz+1]) % p  + ((LL)key * base[ch[1]->sz]) % p + ch[1]->RK) % p;

base代表了28进制数中第i位的位权 这样可以用O(1)的复杂度快速的进行RK值的更新 这本身也是RK算法的一大优势 


对于其中的R操作 只需要将x旋转到根 然后修改值和RK值就可以了


对于其中的I操作 只需要将x旋转到根 将自己接到根的右儿子处 然后把根本来的右儿子变为自己的右儿子并重新计算自己和根的RK值即可


对于其中的Q操作 我们无法直接确定最大前缀和的长度 所以需要枚举 因为长度存在单调性 可以使用二分 

对于每一个长度L 将x-1旋转到根 x+L-1旋转到根的右儿子处 那么长度为L的前缀字符串就正好在x+L-1的左子树 可以直接取出RK值 对于y进行相同的操作然后进行RK值匹配即可


对于选取的MOD值来说 大质数冲突的概率非常小 所以可以认为不会发生冲突 因此可以不必在RK值相同时进行暴力匹配 如果进行暴力匹配可能会导致时间超限 因为匹配可能进行相当多次


至此 本题得到解决 时间上也不会超出限制(虽然我的渣代码离超时也只差几百毫秒)


#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long LL;
const int MAXN = 100000;
const int p = 199999;
int n, base[MAXN+10];
char s[MAXN+10];
struct Node {
    int key, sz, cnt, RK;
    Node *ch[2], *fa;
    Node () {}
    Node(int x, int y, int z) {
        key = x; sz = y; cnt = z;
    }
    void Update() {
        sz = ch[0]->sz + ch[1]->sz + cnt;
        RK = ( ((LL)ch[0]->RK * base[ch[1]->sz+1]) % p  +
                  ((LL)key * base[ch[1]->sz]) % p +
                  ch[1]->RK) % p;
    }
    int cmp(int v) const {
        if(v == key) return -1;
        return v < key ? 0 : 1;
    }
}nil(0, 0, 0), *NIL = &nil;
struct Splay_Tree
{
    Node *root;
    Node tree[MAXN+10];
    int ncnt;
    void init() { root = NIL; ncnt = 0 ;};
    Node *NewNode() {
        Node &t = tree[++ncnt];
        t.ch[0] = t.ch[1] = t.fa = NIL;
        t.sz = t.cnt = 1;
        return &tree[ncnt];
    }
    void Rotate(Node *x, int d)
    {
        Node *y = x->fa;
        y->ch[!d] = x->ch[d];
        if(x->ch[d] != NIL) x->ch[d]->fa = y;
        x->fa = y->fa;
        if(y->fa != NIL) y->fa->ch[y->fa->ch[1] == y] = x;
        x->ch[d] = y;
        y->fa = x;
        if(y == root) root = x;
        y->Update();
    }
    void splay(Node *x, Node *target)
    {
        Node *y, *z;
        while(x->fa != target)
        {
            y = x->fa;
            z = y->fa;
            if(z == target) {
                if(x == y->ch[0]) Rotate(x, 1);
                else Rotate(x, 0);
            }
            else
            {
                if(y == z->ch[0])
                    if(x == y->ch[0])
                        Rotate(y, 1), Rotate(x, 1);
                    else
                        Rotate(x, 0), Rotate(x, 1);
                else
                    if(x == y->ch[1])
                        Rotate(y, 0), Rotate(x, 0);
                    else
                        Rotate(x, 1), Rotate(x, 0);
            }
            x->Update();
        }
    }
    Node *Find_kth(Node * target, int k)
    {
        if(k <= target->ch[0]->sz) return Find_kth(target->ch[0], k);
        if(k <= target->ch[0]->sz+1) return target;
        return Find_kth(target->ch[1], k-target->ch[0]->sz-1);
    }
    void Build(Node * &target, int l, int r, Node *F)
    {
        if(r < l) 
            return ;
        target = NewNode();
        int mid = (l + r) >> 1;
        target->key = s[mid];
        target->fa = F;
        Build(target->ch[0], l, mid-1, target);
        Build(target->ch[1], mid+1, r, target);
        target->Update();
    }
    Node *Find_kth(int k) { Node *t = Find_kth(root, k); splay(t, NIL); return t; }
};
Splay_Tree sp;
int Find_RK(int l, int r)
{
    Node *t1 = sp.Find_kth(l-1);
    Node *t2 = sp.Find_kth(r+1);
    sp.splay(t1, NIL);
    sp.splay(t2, t1);
    return t2->ch[0]->RK;
}
bool check(int x, int y, int L)
{
    int a, b;
    a = Find_RK(x, x + L - 1);
    b = Find_RK(y, y + L - 1);
    return a == b;
}
void R(int x, int k)
{
    Node *target = sp.Find_kth(x);
    sp.splay(target, NIL);
    target->key = k;
    target->Update();
}
void I(int x, int key)
{
    Node *target = sp.Find_kth(x);
    sp.splay(target, NIL);
    Node *New = sp.NewNode();
    New->key = key; New->fa = target;
    New->ch[1] = target->ch[1];
    New->Update();
    if(New->ch[1] != NIL) New->ch[1]->fa = New;
    target->ch[1] = New;
    target->Update();
    n++;
    base[n] = base[n-1] * 28;
    while(base[n] > p) base[n] -= p;
}
  
void Q(int x, int y)
{
    int l = 1, r = min(n-x, n-y), mid, ans = 0;
    while(l <= r)
    {
        mid = (l + r) >> 1;
        if(check(x, y, mid)) {
            ans = max(ans, mid);
            l = mid+1;
        }
        else
            r = mid-1;
    }
    printf("%d\n", ans);
}
int main()
{
    int m;
    scanf("%s", s+1);
    n = strlen(s+1);
    for(int i = 1; i <= n; i++) s[i] -= 'a';
    s[0] = s[n+1] = 27;
    base[0] = 1; base[1] = 28;
    for(int i = 2; i <= n+2; i++) base[i] = (base[i-1] * 28) % p;
    sp.Build(sp.root, 0, n+1, NIL);
    n += 2;
    scanf("%d", &m);
    for(int i = 1; i <= m; i++)
    {
        int x, y;
        scanf("%s", s);
        if(s[0] == 'R') {
            scanf("%d",&x);
            if(x > n-2) continue;
            x++;
            scanf("%s", s);
            R(x,s[0]-'a');
        }
          
        else if(s[0] == 'I') {
            scanf("%d",&x);
            x++;
            scanf("%s", s);
            I(x,s[0]-'a');
        }
          
        else {
            scanf("%d%d",&x,&y);
            x++;y++;
            Q(x,y);
        }
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值