[BZOJ]1014 [JSOI]2008 火星人prefix Splay

本文介绍了一种名为火星人prefix的算法,该算法用于解决字符串匹配问题中的公共前缀查询及字符串动态修改挑战。通过使用Splay树动态维护hash值的方法,文章详细解释了如何高效地处理包括查询、修改和插入等操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1014: [JSOI2008]火星人prefix

Time Limit: 10 Sec Memory Limit: 162 MB
Submit: 8170 Solved: 2592
[Submit][Status][Discuss]
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个数据,没有插入操作。

Source

[Submit][Status][Discuss]

HOME Back

题解

正确指法练的稍微熟一点, 能写题了… 过不了多久应该就要熟练了.
原来还以为用什么后缀平衡树… 果然还是自己Naive, 字符串匹配还可以用hash啊… 由于插入说明需要动态维护hash值, 那我们就想到了平衡树。 由于求最长前缀, 那么还需要二分前缀长度看前缀hash值是否一样来找出最长前缀—-这就说明我们需要动态且快速找出一段字符串的hash值, 那么就是Splay啦!! 用Splay来动态维护hash值, 每次提取区间都是拿手好戏了.
用%好慢啊, 卡时过… 改成unsigned自然溢出快了一倍.

#include<stdio.h>
#include<cstring>
#include<algorithm>
using namespace std;
#define Boc register char
#define Acce register int
unsigned int mod = 137;
const int maxn = 150015;
int n, m, tot, root;
char ex[2], ss[2], s[maxn];
int pw[maxn], fa[maxn], vv[maxn];
int siz[maxn], c[maxn][2];
unsigned int v[maxn], h[maxn];
inline void init()
{
    pw[0] = 1;
    for (int i = 1; i < maxn; ++ i)
        pw[i] = pw[i - 1] * mod;
}
inline const int read()
{
    Acce x = 0;
    Boc ch = getchar();
    while (ch < '0' || ch > '9') ch = getchar();
    while (ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
    return x;
}
inline void update(int x)
{
    int l = c[x][0], r = c[x][1];
    siz[x] = siz[l] + siz[r] + 1;
    h[x] = h[l] + v[x] * pw[siz[l]] + h[r] * pw[siz[l] + 1];
}
inline void rotate(int x, int &wanna)
{
    int y = fa[x], z = fa[y];
    int l = (c[y][0] != x), r = l ^ 1;
    (y != wanna) ? c[z][c[z][0] != y] = x : wanna = x;
    fa[x] = z, fa[y] = x, fa[c[x][r]] = y;
    c[y][l] = c[x][r], c[x][r] = y;
    update(y), update(x);
}
inline void splay(int x, int &wanna)
{
    for (int f; x != wanna; rotate(x, wanna))
        if ((f = fa[x]) != wanna)
            rotate((c[fa[f]][0] == f ^ c[f][0] == x) ? x : f, wanna);
}
void insert(int &x, int k, int f, int fix)
{
    if (!x)
    {
        x = ++ tot, fa[x] = f, siz[x] = 1, v[x] = h[x] = fix;
        splay(x, root); return;
    }
    if (k >= siz[c[x][0]] + 1) insert(c[x][1], k - siz[c[x][0]] - 1, x, fix);
    else insert(c[x][0], k, x, fix);
}
int find(int x, int k)
{
    if (siz[c[x][0]] + 1 == k) return x;
    else if (siz[c[x][0]] >= k) return find(c[x][0], k);
    else return find(c[x][1], k - siz[c[x][0]] - 1);
}
void build(int &x, int f, int L, int R)
{
    if (L > R) return;
    int mid = (L + R) >> 1;
    x = ++ tot;
    fa[x] = f, v[x] = vv[mid];
    build(c[x][0], x, L, mid - 1), build(c[x][1], x, mid + 1, R);
    update(x);
}
inline int calc(int x, int len)
{
    splay(find(root, x), root);
    splay(find(root, x + len + 1), c[root][1]);
    return h[c[c[root][1]][0]];
}
inline int query(int x, int y)
{
    int lf = 1, rg = min(tot - x, tot - y) - 1, ans = 0;
    while (lf <= rg)
    {
        int mid = (lf + rg) >> 1;
        if (calc(x, mid) == calc(y, mid)) lf = mid + 1, ans = mid;
        else rg = mid - 1;
    }
    return ans;
}
int main()
{
    init();
    scanf("%s", s + 2);
    n = strlen(s + 2), m = read();
    s[1] = s[n + 2] = 'a';
    for (Acce i = 1; i <= n + 2; ++ i) vv[i] = s[i] - 'a' + 1;
    build(root, 0, 1, n + 2);
    for (Acce i = 1; i <= m; ++ i)
    {
        scanf("%s", ss);
        int x = read();
        if (ss[0] == 'Q')
        {
            int y = read();
            printf("%d\n", query(x, y));
            continue;
        }
        scanf("%s", ex);
        if (ss[0] == 'R')
        {
            x = find(root, x + 1), splay(x, root);
            v[root] = ex[0] - 'a' + 1, update(root);
        }
        if (ss[0] == 'I') insert(root, x + 1, 0, ex[0] - 'a' + 1);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值