线段树维护字符串哈希

J 科学幻想
闲来补以前不会的题,打眼一看有了大致思路,写好大致的样子后,调了调一些bug,过了样例后提交,wa了。
昨晚改到现在,终于发现最终的两处错误在于,查询区间哈希值,二分查询最长长度。
第一处,查询[x,y]区间的哈希值,往下二分的时候,y的值不能直接代,要修改成mid才能往下代。因为两段字符串哈希值的合并,和后一段字符串的长度有关。
第二处,二分查找两个区间从左端点开始,能够匹配到的最长长度,可能存在两个左端点就不相等的情况,最后查询到的l的值是1,这时候就要特判1.

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e5 + 5, mod = 1e9 + 7;
char s[N];
unsigned long long f[4 * N], P[4 * N];
int n;
void build(int p, int l, int r)
{
    if (l == r) {
        f[p] = s[l];
        return;
    }
    int mid = (l + r) / 2;
    int chl = 2 * p, chr = 2 * p + 1;
    build(chl, l, mid);
    build(chr, mid + 1, r);
    f[p] = f[chl] * P[r - mid] + f[chr];
}
void change(int p, int l, int r, int x, char y)
{
    if (l == r) {
        f[p] = y;
        return;
    }
    int mid = (l + r) / 2;
    int chl = 2 * p, chr = 2 * p + 1;
    if (x <= mid) change(chl, l, mid, x, y);
    if (x > mid) change(chr, mid + 1, r, x, y);
    f[p] = f[chl] * P[r - mid] + f[chr];
}
unsigned long long que(int p, int l, int r, int x, int y)
{
    if (x <= l && r <= y) return f[p];
    int mid = (l + r) / 2;
    int chl = 2 * p, chr = 2 * p + 1;
    if (y <= mid) return que(chl, l, mid, x, y);
    if (x > mid) return que(chr, mid + 1, r, x, y);
    return que(chl, l, mid, x, mid) * P[y - mid] + que(chr, mid + 1, r, x, y);
    //这个return是第一处bug
}
bool ck(int l1, int r1, int l2, int r2)
{
    int l = 1, r = r1 - l1 + 1;
    while(l < r) {
        int mid = (l + r + 1) >> 1;
        int h1 = que(1, 1, n, l1, l1 + mid - 1);
        int h2 = que(1, 1, n, l2, l2 + mid - 1);
        if (h1 == h2) l = mid;
        else r = mid - 1;
    }
    //这个l==1的特判是第二个bug
    if (l == 1) {
        if (r1 - l1 + 1 == 1) return 1;
        else if(s[l1] != s[l2]) {
            return que(1, 1, n, l1 + 1, r1) == que(1, 1, n, l2 + 1, r2);
        }
    }
    if (l == r1 - l1 + 1 || l == r1 - l1) return 1;
    int h1 = que(1, 1, n, l1 + l + 1, r1);
    int h2 = que(1, 1, n, l2 + l + 1, r2);
    // cout << h1 << " " << h2 << endl;
    return h1 == h2;
}
signed main()
{
    int m;
    cin >> n >> m;
    // scanf("%s", s + 1);
    cin >> (s + 1);
    P[0] = 1;
    for (int i = 1; i <= n; i++) P[i] = P[i - 1] * 13331;
    build(1, 1, n);
    for (int i = 1; i <= m; i++) {
        int op;
        scanf("%lld", &op);
        if (op == 1) {
            int x;
            char y;
            cin >> x >> y;
            s[x] = y;
            change(1, 1, n, x, y);
        }
        else {
            int l1, r1, l2, r2;
            scanf("%lld%lld%lld%lld", &l1, &r1, &l2, &r2);
            if (r1 - l1 == r2 - l2) {
                if (ck(l1, r1, l2, r2)) cout << "YES" << endl;
                else cout << "NO" << endl;
            }
            else cout << "NO" << endl;
        }
    }
    return 0;
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值