URAL - 1989 Subpalindromes (单点修改,查询某个区间子串是否为回文串) (线段树+哈希)

链接

You have a string and queries of two types:
replace i’th character of the string by character a;
check if substring sj…sk is a palindrome.
Input
The first line contains a string consisting of n small English letters. The second line contains an integer m that is the number of queries (5 ≤ n, m ≤ 10 5). The next m lines contain the queries.
Each query has either form “change i a”, or “palindrome? j k”, where i, j, k are integers (1 ≤ i ≤ n; 1 ≤ j ≤ k ≤ n), and character a is a small English letter.
Output
To all second type queries, you should output “Yes” on a single line if substring s j… s k is a palindrome and “No” otherwise.
Example
input output
abcda
5
palindrome? 1 5
palindrome? 1 1
change 4 b
palindrome? 1 5
palindrome? 2 4
No
Yes
Yes
Yes

题意很简单,就是单点更新某个字符,然后询问某个区间是否为回文串。

思路:首先想到利用哈希值来判断回文串,如果左右哈希值相同就是回文串。所以这题我们使用线段树,维护这个区间的左右哈希值,最后再查询,注意最后查询的时候要记录查询到当前区间时,已经查询到的长度。因为这要作为幂值
主要就是对线段树的变形,注意这个幂值。
字符串的哈希是 Hash(str[l,r]) = str[l]+str[l+1]*B + str[l+2]*B^2 + …. + str[r]*B^(r-l);
所以字符串PushUp的时候也是记录左右的长度。

int L = tree[lson].len();
    int R = tree[rson].len();
    tree[k].Lv = tree[lson].Lv + tree[rson].Lv*Pow[L];
    tree[k].Rv = tree[lson].Rv*Pow[R] + tree[rson].Rv;

代码如下:

#include<bits/stdc++.h>

using namespace std;
typedef unsigned long long ull;
const ull B = 1e8+7;
const int MAX = 1e5+10;
#define lson (k*2)
#define rson (k*2+1)
char str[MAX];
ull Pow[MAX];
class Node{
public:
    int l,r;
    ull Lv,Rv;
    Node();
    int len(){
        return r-l+1;
    }
    int mid(){
        return (l+r)/2;
    }
};
Node tree[MAX*4];
void init(){
    Pow[0] = 1;
    for(int i=1;i<=(int)1e5;++i)
        Pow[i] = Pow[i-1]*B;
}
void PushUp(int k){
    int L = tree[lson].len();
    int R = tree[rson].len();
    tree[k].Lv = tree[lson].Lv + tree[rson].Lv*Pow[L];
    tree[k].Rv = tree[lson].Rv*Pow[R] + tree[rson].Rv;
}
void Build(int L,int R,int k){
    tree[k].l = L;
    tree[k].r = R;
    if(L == R){
        tree[k].Lv = (ull)str[L];
        tree[k].Rv = (ull)str[L];
        return;
    }
    int mid = (L+R)/2;
    Build(L,mid,lson);
    Build(mid+1,R,rson);
    PushUp(k);
}
void Update(int p,int k){
    if(tree[k].l == tree[k].r && tree[k].l == p){
        tree[k].Lv = (ull)str[p];
        tree[k].Rv = (ull)str[p];
        return;
    }
    int mid = tree[k].mid();
    if(p <= mid)    Update(p,lson);
    else    Update(p,rson);
    PushUp(k);
}
//这里要每个递归,都知道当前已经找到的区间值,所以是引用,或者指针。
ull QueryL(int L,int R,int k,int &len){
    if(L <= tree[k].l && tree[k].r <= R){
        ull temp = tree[k].Lv*Pow[len];
        len += tree[k].len();
        return temp;
    }
    int mid = tree[k].mid();
    ull res = 0;
    if(L <= mid)    res += QueryL(L,R,lson,len);
    if(R > mid)     res += QueryL(L,R,rson,len);
    return res;
}

ull QueryR(int L,int R,int k,int &len){
    if(L <= tree[k].l && tree[k].r <= R){
        ull temp = tree[k].Rv*Pow[len];
        len += tree[k].len();
        return temp;
    }
    int mid = tree[k].mid();
    ull res = 0;
    if(R > mid) res += QueryR(L,R,rson,len);
    if(L <= mid)    res += QueryR(L,R,lson,len);
    return res;
}
int main(void){
    init();
    scanf("%s",str+1);
    int N = strlen(str+1);
    Build(1,N,1);
    int Q;
    scanf("%d",&Q);
    char op[20];
    while(Q--){
        scanf("%s",op);
        if(strcmp(op,"palindrome?") == 0){
            int l,r;
            scanf("%d%d",&l,&r);
            int len = 0;
            ull Lv = QueryL(l,r,1,len);
            len = 0;
            ull Rv = QueryR(l,r,1,len);
            if(Lv == Rv)
                printf("Yes\n");
            else
                printf("No\n");
        }
        else{
            int op;
            char ch;
            scanf("%d %c",&op,&ch);
            str[op] = ch;
            Update(op,1);
        }
    }
    return 0;
}
Node::Node(){
    l = r = 0;
    Lv = Rv = 0;
}

猜你喜欢

转载自blog.csdn.net/zhao5502169/article/details/81451830