URAL 1989 Subpalindromes(哈希+线段树)

题意:给你一个字符串,再给出两种操作,一种是给出一个区间判断这个区间是不是回文串,一种是将字符串的某个字符改成另一个。

思路:一开始看到给出的时间只有500ms就蒙了,然后就去翻了下博客,说一下自己解题过程中遇到的疑惑。

1.这题的哈希多项式公式好奇怪,和之前所做的题的多项式不一样。之前的多项式是字符串的幂次方随着字符串增长而递减的,这题却是递增的。
(这题的公式:h[i]=s[1]+s[2]*p+s[3]*p^2+…..+s[i]*p^(i-1))做完题之后大胆猜测(maybe是方便做线段树的单点更新和区间求和操作?

因为哈希多项式的改变,所以给出两个连续区间的哈希公式,求整个区间的哈希值的公式也要改变。
(加入左边区间的范围是[l,m],右边是[m+1,r],那么整个区间的哈希值是h(l,m)+p^(m-l+1))*h(m+1,r))这个是从左算起的,还有一个从右算起的这里就不写了。

2.还有一个问题是怎么去判断是不是回文串,这里给出了一种很巧妙的方法,就是建立两棵线段树,一棵是按照字符串正向建,另一颗反向建,因此判断是不是回文,只要比较在两棵树中,正向和反向的区间哈希值是不是相等就行了。

思路大致就是如此,下面是ac代码:

#include <cstdio>
#include <cstring>
#include <string>
#include  <iostream>
#include <algorithm>
#include <map>
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
typedef unsigned long long ull;
using namespace std;
const int maxn = 1e5 + 5;
const int has = 131;
char str[maxn];
int len, n;
char op[20];
ull sum[2][maxn << 2];
ull p[maxn << 2];
// h[maxn << 2];
void pushup(int rt, int l, int r) {
    sum[0][rt] = sum[0][rt << 1] + sum[0][rt << 1 | 1] * p[l];
    sum[1][rt] = sum[1][rt << 1] * p[r] + sum[1][rt << 1 | 1] ;
}
void build(int l, int r, int rt) {
    if(l == r) {
        sum[0][rt] = sum[1][rt] = str[l];
        return ;
    }
    int m = (l + r) >> 1;
    build(lson);
    build(rson);
    pushup(rt, m - l + 1, r - m);
}
void updata(int pos, char a, int l, int r, int rt) {
    if(l == r) {
        str[pos] = a;
        sum[0][rt] = sum[1][rt] = str[pos];
        return ;
    }
    int m = (l + r) >> 1;
    if(m >= pos) updata(pos, a, lson);
    else updata(pos, a, rson);
    pushup(rt, m - l + 1, r - m);
}
ull query(int flag, int L, int R, int l, int r, int rt) {
    if(L <= l && R >= r) {
        return sum[flag][rt];
    }
    int m = (l + r) >> 1;
    ull ans1 = 0, ans2 = 0, ans = 0, t = 0;
    if(L <= m) {//是要对t进行分类讨论的
        t += 1;
        ans1 = query(flag, L, R, lson);
    }
    if(R > m) {
        t += 2;
        ans2 = query(flag, L, R, rson);
    }
    if(t == 1) ans = ans1;
    else if(t == 2) ans = ans2;
    else if(t == 3) {
        if(flag == 0) {
            ans = ans1 + ans2 * p[m - max(l, L) + 1];//这里的max和下面的min是有必要的
        } else if(flag == 1) {
            ans = ans1 * p[min(R, r) - m] + ans2;
        }
    }
    return ans;
}
int main() {
    scanf("%s%d", str + 1, &n);
    len = strlen(str + 1);
    p[0] = 1;
    for(int i = 1; i <= len; i++) p[i] = p[i - 1] * has;
    build(1, len, 1);
    while(n--) {
        int a;
        scanf("%s %d", op, &a);
        if(op[0] == 'p') {
            int b;
            scanf("%d", &b);
            ull a1 = query(0, a, b, 1, len, 1);
            ull a2 = query(1, a, b, 1, len, 1);
            if(a1 == a2) printf("Yes\n");
            else printf("No\n");
        } else if(op[0] == 'c') {
            char b;
            cin >> b;
            updata(a, b, 1, len, 1);
        }
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值