bzoj1014: [JSOI2008]火星人prefix Splay+二分答案与字符串Hash求最长公共前缀

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个数据,没有插入操作。

分析

字符串Hash处理公共前缀。
由于是可修改的插入的,就用Splay处理一下吧。
每个节点上储存本身的值,还有这个子树字符串Hash的值
复杂度 O(nlog2n)

代码

/**************************************************************
    Problem: 1014
    User: 2014lvzelong
    Language: C++
    Result: Accepted
    Time:4100 ms
    Memory:7524 kb
****************************************************************/

#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#include<cmath>
using namespace std;
const int N = 220000;
int read() {
    char ch = getchar(); int x = 0, f = 1;
    while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
    while(ch >= '0' && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();}
    return x * f;
}
char a[N];
int ch[N][2], siz[N], fa[N], root, sz;
unsigned int val[N], h[N], bin[N];
bool wh(int p) {return ch[fa[p]][1] == p;}
void update(int p) {
    int ls = ch[p][0], rs = ch[p][1];
    siz[p] = siz[ls] + siz[rs] + 1;
    h[p] = h[ls] + val[p] * bin[siz[ls]] + h[rs] * bin[siz[ls] + 1];
}
void Rotate(int p) {
    int f = fa[p], g = fa[f], c = wh(p);
    if(g) ch[g][wh(f)] = p; fa[p] = g;
    ch[f][c] = ch[p][c ^ 1]; if(ch[f][c]) fa[ch[f][c]] = f;
    ch[p][c ^ 1] = f; fa[f] = p;
    update(f);
}

void Splay(int p, int tar) {
    for(;fa[p] != tar; Rotate(p))
        if(fa[fa[p]] != tar) Rotate(wh(p) == wh(fa[p]) ? fa[p] : p);
    update(p);
    if(!tar) root = p;
}

void Build(int &p, int L, int R, int f) {
    if(!p) p = ++sz;
    fa[p] = f; int mid = L + R >> 1;
    val[p] = a[mid] - 'a' + 1;
    if(L < mid) Build(ch[p][0], L, mid - 1, p);
    if(R > mid) Build(ch[p][1], mid + 1, R, p);
    update(p);
}

int Kth(int k) {
    for(int p = root, lsize = 0, cur;p; p = ch[p][k > cur]) {
        cur = lsize + siz[ch[p][0]];
        if(k == cur + 1) return p;
        if(k > cur) lsize = cur + 1;
    }
    return -1;
}

void Ins(int k, char c) {
    int f = Kth(k + 1), p = Kth(k + 2);
    Splay(f, 0); Splay(p, f);
    ch[p][0] = ++sz; val[ch[p][0]] = c - 'a' + 1; fa[ch[p][0]] = p;
    update(ch[p][0]); update(p); update(f);
}

int Query(int L, int R) {
    int f = Kth(L); Splay(f, 0);
    int p = Kth(R + 2); Splay(p, f);
    return h[ch[p][0]];
}

void Modify(int k, char c) {Splay(Kth(k + 1), 0); val[root] = c - 'a' + 1; update(root);}

int Solve(int L, int R) {
    int l = 1, r = min(sz - L, sz - R) - 1, ans = 0;
    while(l <= r) {
        int mid = l + r >> 1;
        if(Query(L, L + mid - 1) == Query(R, R + mid - 1)) {
            ans = mid; l = mid + 1;
        }
        else r = mid - 1;
    }
    return ans;
}

int main() { 
    bin[0] = 1; for(int i = 1;i <= 200000; ++i) bin[i] = bin[i - 1] * 27;
    scanf("%s", a + 2); int n = strlen(a + 2);
    Build(root, 1, n + 2, 0);
    int m = read();
    char opt[2], d[2];
    while(m--) {
        scanf("%s", opt + 1); int x = read();
        if(opt[1] == 'Q') printf("%d\n", Solve(x, read()));
        if(opt[1] == 'R') scanf("%s", d + 1), Modify(x, d[1]);
        if(opt[1] == 'I') scanf("%s", d + 1), Ins(x, d[1]);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值