[JSOI2008] 火星人prefix

\(\text{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\) 函数的值。

\(\text{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\) 不超过当前字符串长度。

\(\text{Output}\)

对于输入文件中每一个询问操作,你都应该输出对应的答案。一个答案一行。

\(\text{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

\(\text{Sample Output}\)

5
1
0
2
1

数据规模:

对于 \(100%\) 的数据,满足:

1、 所有字符串自始至终都只有小写字母构成。

2、 \(M <= 150,000\)

3、 字符串长度 \(L\) 自始至终都满足 \(L <= 100,000\)

4、 询问操作的个数不超过 \(10,000\) 个。

对于第 \(1,2\) 个数据,字符串长度自始至终都不超过 \(1,000\)
对于第 \(3,4,5\) 个数据,没有插入操作。

\(\text{Solution:}\)

维护一颗splay区间树,维护一个节点为根的子树中序遍历的字符串的哈希值。

\(LCP\) 就二分长度,然后比较。

#define ull unsigned long long
const int maxN = 5e5 + 10;
const int base = 27;
ull xp[maxN], H[maxN];
int n, m;
int fa[maxN], ch[maxN][2], val[maxN], size[maxN], Ncnt, root, sz;
#define ls(x) (ch[x][0])
#define rs(x) (ch[x][1])
#define chk(x) (ch[fa[x]][1] == x)
inline void pushup(int x) {
    size[x] = size[ls(x)] + size[rs(x)] + 1;
    H[x] = H[ls(x)] * xp[size[rs(x)] + 1] + val[x] * xp[size[rs(x)]] + H[rs(x)];
}
inline void rotate(int x) {
    int y = fa[x], z = fa[y], k = chk(x), tmp = ch[x][k ^ 1];
    ch[y][k] = tmp, fa[tmp] = y;
    ch[z][chk(y)] = x, fa[x] = z;
    ch[x][k ^ 1] = y, fa[y] = x;
    pushup(y), pushup(x);
}
inline void splay(int x, int goal = 0) {
    int y, z;
    while (fa[x] != goal) {
        y = fa[x], z = fa[y];
        if (z != goal) {
            if (chk(x) == chk(y)) rotate(y);
            else rotate(x);
        }
        rotate(x);
    }
    if (goal == 0) root = x;
}
inline int kth(int k) {
    int x = root;
    while (1) {
        if (ch[x][0] && k <= size[ls(x)])
            x = ls(x);
        else if (size[ls(x)] + 1 < k) {
            k -= size[ls(x)] + 1, x = rs(x);
        } else return x;
    }
}
inline int split(int x, int y) {
    int pre = kth(x - 1), nxt = kth(y + 1);
    splay(pre); splay(nxt, pre);
    return ls(nxt);
}
inline void insert(int x, char str) {
    int pre = kth(x), nxt = kth(x + 1);
    splay(pre); splay(nxt, pre);
    int cur = ++Ncnt;
    ch[nxt][0] = cur;
    val[cur] = H[cur] = str - 'a' + 1;
    size[cur] = 1;
    fa[cur] = nxt;
    splay(cur);
}
inline ull query(int x, int L) {
    int cur = split(x, x + L - 1);
    return H[cur];
}
inline int solve(int x, int y) {
    int l = 1, r = min(sz - x, sz - y), mid, ans = 0;
    while (l <= r) {
        mid = (l + r) >> 1;
        if (query(x, mid) == query(y, mid)) ans = mid, l = mid + 1;
        else r = mid - 1;
    }
    return ans;
}
int build(int l, int r, char *cher) {
    if (l > r) return 0;
    int mid = (l + r) >> 1;
    int x = ++Ncnt;
    size[x] = 1;
    val[x] = H[x] = cher[mid] - 'a' + 1;
    if (ch[x][0] = build(l, mid - 1, cher)) fa[ch[x][0]] = x;
    if (ch[x][1] = build(mid + 1, r, cher)) fa[ch[x][1]] = x;
    pushup(x);
    return x;
}
char str[maxN];
int main() {
#ifndef ONLINE_JUDGE
    file("P4036");
#endif
    xp[0] = 1;
    for (int i = 1; i < maxN; ++ i) xp[i] = xp[i - 1] * base;

    scanf("%s", str + 2);
    n = strlen(str + 2); sz = n + 2;
    root = build(1, n + 2, str);


    scanf("%d", &m);
    char op[30], s[3];
    while (m --) {
        scanf("%s", op);
        if (op[0] == 'Q') {
            int x, y; scanf("%d%d", &x, &y);
            printf("%d\n", solve(x + 1, y + 1));
        } else if (op[0] == 'R') {
            int x; scanf("%d%s", &x, s);
            int cur = kth(x + 1); splay(cur);
            val[cur] = s[0] - 'a' + 1;
            pushup(cur);
        } else {
            int x; scanf("%d%s", &x, s);
            insert(x + 1, s[0]);
            sz++;
        }
    }
}

猜你喜欢

转载自www.cnblogs.com/HNYLMSTea/p/10545871.html