[BZOJ1014][JSOI2008]火星人prefix

[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、 询问。语法:Q x y,x, y均为正整数。功能:计算LCQ(x, y) 限制:1 <= x, y <= 当前字符串长度。 2、 修改。语法:R x d,x是正整数,d是字符。功能:将字符串中第x个数修改为字符d。限制:x不超过当前字符串长度。 3、 插入:语法:I x d,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
数据规模:
对于100%的数据,满足:
1、 所有字符串自始至终都只有小写字母构成。
2、 M <= 150,000
3、 字符串长度L自始至终都满足L <= 100,000
4、 询问操作的个数不超过10,000个。
对于第1,2个数据,字符串长度自始至终都不超过1,000
对于第3,4,5个数据,没有插入操作。
Source
JSOI

Solution
-Wall -Shadow -Extra

Code

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
typedef unsigned int UI;
#define rep(i, l, r) for (int i = (l); i <= (r); i++)
#define per(i, r, l) for (int i = (r); i >= (l); i--)
#define REP(i, n) for (int i = 0; i < (n); i++)
#define PER(i, n) for (int i = (n)-1; i >= 0; i--)
#define stack _stack

const int N = 111111;
const int INF = ~0U>>2;
const UI SEED = 131;
template<typename T> inline void read(T &x){
    x = 0; T f = 1; char ch = getchar();
    while (!isdigit(ch)) {if (ch == '-') f = -1; ch = getchar();}
    while (isdigit(ch))  {x = x * 10 + ch - '0'; ch = getchar();}
    x *= f;
}
template<typename T> inline void write(T &x){
    static T a[20]; int ptr = 0;
    if (!x) {puts("0"); return;}
    if (x < 0){putchar('-'); x = -x;}
    while (x) {a[ptr++] = x % 10; x /= 10;}
    per(i, ptr-1, 0) putchar('0' + a[i]);
    putchar('\n');
}

char str[N];
UI P[N];
struct node{
    node *ch[2], *p; UI key; int sz, val;
    bool d(){return this == p->ch[1];}
    void sc(node *_c, int _d) {ch[_d] = _c; _c->p = this;}
}*Null, *root;
inline void push_up(node *t){
    t->sz = t->ch[0]->sz + t->ch[1]->sz + 1;
    UI ret = t->ch[0]->key; ret = ret * SEED + t->val;
    ret *= P[t->ch[1]->sz]; ret += t->ch[1]->key;
    t->key = ret;
}

node mem[N];
node *stack[N]; int top;
inline void init_memory() {REP(i, N) stack[i] = mem + i; top = N;}
inline node *new_node() {return stack[--top];}
inline node *new_node(int v){
    node *ret = new_node();
    ret->val = ret->key = v; ret->sz = 0;
    ret->ch[0] = ret->ch[1] = ret->p = Null;
    return ret;
}
inline void init_null(){ 
    Null = new_node(0);
    Null->sz = Null->key = Null->val = 0;
    Null->p = Null->ch[0] = Null->ch[1] = Null;
}

inline void rot(node *t){
    node *p = t->p; int d = t->d();
    p->sc(t->ch[!d], d);
    if (p-p==Null) t->p == Null; else p->p->sc(t, p->d());
    t->sc(p, !d); push_up(p); push_up(t);
    if (p == root) root = t;
}
inline void splay(node *t, node *fa){
    for(; t->p != fa;){
        if (t->p->p == fa) rot(t);
        else t->d() == t->p->d()?(rot(t->p), rot(t)):(rot(t), rot(t));
    }
    push_up(t);
}
inline node *find(int k){
    for (node *t = root; ;){
        int cnt = t->ch[0]->sz;
        if (cnt == k) return t;
        t = t->ch[k > cnt];
        if (k > cnt) k -= cnt+1; 
    }
}
node *&getseq(int l, int r){
    node *L = find(l); splay(L, Null);
    node *R = find(r); splay(R, root);
    return root->ch[1]->ch[0];
}
inline void Insert(int v, int pos){
    node *t = new_node(v);
    getseq(pos, pos + 1);
    root->ch[1]->sc(t, 0);
    splay(t, Null);
}
inline void Replace(int v, int pos){
    node *t = getseq(pos-1, pos+1); t->val = t->key = v;
    splay(t, Null); 
}
inline void build(){
    init_memory(); init_null(); root = new_node(0); 
    node *end = new_node(0); root->sc(end, 1);
    splay(end, Null);
}
inline UI Cal(int l, int r){ node *t = getseq(l-1, r+1); return t->key; }
inline void Query(int l, int r){
    int ret = 0; int n = root->sz-2;
    for(int i = (1<<18); i > 0; i >>= 1){
        if(l+i-1<=n && r+i-1<=n && Cal(l, l+i-1) == Cal(r, r+i-1)){
            ret += i; l += i; r += i;
        }
    }
    write(ret);
}
int main(){
    scanf("%s", str); int n = strlen(str);
    P[0] = 1; rep(i, 1, N) P[i] = P[i-1] * SEED; 
    build(); REP(i, n) Insert(str[i], i);

    int m; read(m);
    while (m--){
        char opt[20]; int pos, v; char ch; scanf("%s", opt);
        switch (opt[0]){
            case 'I': read(pos); ch = getchar(); Insert(ch, pos); break;
            case 'Q': read(pos); read(v); Query(pos, v); break;
            case 'R': read(pos); ch = getchar(); Replace(ch, pos); break;
        }
    }
    return 0;   
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值