F - Palindrome Query

F - Palindrome Query

Problem Statement

You are given a string S of length N consisting of lowercase English letters.
Process Q queries described below in the order they are given.
There are two types of queries:

  • 1 x c : Change the x-th character of S to the lowercase English letter c.
  • 2 L R : If the substring formed by the L-th through R-th characters of S is a palindrome, print Yes; otherwise, print No.

Constraints

  • 1N106
  • 1Q105
  • S is a string of length N consisting of lowercase English letters.
  • 1xN
  • c is a lowercase English letter.
  • 1LRN
  • N,Q,x,L,R are integers.

Input

The input is given from Standard Input in the following format. Here, queryi is the i-th query to be processed.

N Q
S
query1
query2

queryQ

Each query is given in one of the following formats:

1 x c

2 L R

Output

Follow the instructions in the problem statement and print the answers to the queries, separated by newlines.


Sample Input 1

7 8
abcbacb
2 1 5
2 4 7
2 2 2
1 5 c
2 1 5
2 4 7
1 4 c
2 3 6

Sample Output 1

Yes
No
Yes
No
Yes
Yes

Initially, S= abcbacb.
For the first query, the string formed by the 1-st through 5-th characters of S is abcba, which is a palindrome. Thus, print Yes.
For the second query, the string formed by the 4-th through 7-th character of S is bacb, which is not a palindrome. Thus, print No.
For the third query, the string formed by the 2-nd through 2-nd character of S is b, which is a palindrome. Thus, output Yes.
For the fourth query, change the 5-th character of S to c. S becomes abcbccb.
For the fifth query, the string formed by the 1-st through 5-th character of S is abcbc, which is not a palindrome. Thus, output No.
For the sixth query, the string formed by the 4-th through 7-th character of S is bccb, which is a palindrome. Thus, output Yes.
For the seventh query, change the 4-th character of S to c. S becomes abccccb.
For the eighth query, the string formed by the 3-rd through 6-th character of cccc, which is a palindrome. Thus, output Yes.

 

解题思路

  做法是用线段树来维护子串的字符串哈希值。

  对于字符串 s 的子串 s[l,r]=sl,sl+1,,sr,将其翻转得到 s[l,r]=sr,sr1,,sl,那么子串 s[l,r] 是回文串等价于 s[l,r]=s[l,r]。对与判断两个字符串是否相同可以转而判断两个字符串的字符串哈希是否相同,其中 s[l,r] 的字符串哈希值是i=lrsi×P(ri)(mod2641)

  一般取 P=13331si 指字符对应的 ASCII 码。

  同理 s[l,r] 的字符串哈希值是i=lrsi×P(il)(mod2641)

  我们只需要用线段树来维护某个区间所表示的子串和其翻转后的子串的字符串哈希值即可,线段树实现支持的操作有单点修改,区间查询。即对于表示区间 [l,r] 的节点,维护 s[l,r] 的字符串哈希值 h1s[l,r] 的字符串哈希值 h2。节点的更新操作相当于将左右儿子的字符串拼接然后求字符串哈希。对于 h1,假设其左儿子维护的 s[l,mid] 的字符串哈希值是 h1,右儿子维护的 s[mid+1,r] 的字符串哈希值是 h1,则有 h1=h1×Prmid+h1。同理有 h2=h2×Pmidl+1+h2。查询操作就是询问子串 s[l,r]h1s[l,r]h2 是否相同。

  AC 代码如下,时间复杂度为 O(n+qlogn)

#include <bits/stdc++.h>
using namespace std;

typedef unsigned long long ULL;

const int N = 1e6 + 10, P = 13331;

char s[N];
ULL p[N];
struct Node {
    int l, r;
    ULL h1, h2;
}tr[N * 4];

void pushup(int u) {
    tr[u].h1 = tr[u << 1].h1 * p[tr[u << 1 | 1].r - tr[u << 1 | 1].l + 1] + tr[u << 1 | 1].h1;
    tr[u].h2 = tr[u << 1 | 1].h2 * p[tr[u << 1].r - tr[u << 1].l + 1] + tr[u << 1].h2;
}

void build(int u, int l, int r) {
    tr[u] = {l, r};
    if (l == r) {
        tr[u].h1 = tr[u].h2 = s[l];
    }
    else {
        int mid = l + r >> 1;
        build(u << 1, l, mid);
        build(u << 1 | 1, mid + 1, r);
        pushup(u);
    }
}

void modify(int u, int x, int c) {
    if (tr[u].l == tr[u].r) {
        tr[u].h1 = tr[u].h2 = c;
    }
    else {
        int mid = tr[u].l + tr[u].r >> 1;
        if (x <= mid) modify(u << 1, x, c);
        else modify(u << 1 | 1, x, c);
        pushup(u);
    }
}

Node query(int u, int l, int r) {
    if (tr[u].l >= l && tr[u].r <= r) return tr[u];
    int mid = tr[u].l + tr[u].r >> 1;
    if (r <= mid) return query(u << 1, l, r);
    else if (l >= mid + 1) return query(u << 1 | 1, l, r);
    Node t1 = query(u << 1, l, r), t2 = query(u << 1 | 1, l, r);
    ULL h1 = t1.h1 * p[t2.r - t2.l + 1] + t2.h1;
    ULL h2 = t2.h2 * p[t1.r - t1.l + 1] + t1.h2;
    return {t1.l, t2.r, h1, h2};
}

int main() {
    int n, m;
    scanf("%d %d %s", &n, &m, s + 1);
    p[0] = 1;
    for (int i = 1; i <= n; i++) {
        p[i] = p[i - 1] * P;
    }
    build(1, 1, n);
    while (m--) {
        int op;
        scanf("%d", &op);
        if (op == 1) {
            int x;
            char c[5];
            scanf("%d %s", &x, c);
            modify(1, x, c[0]);
        }
        else {
            int l, r;
            scanf("%d %d", &l, &r);
            Node t = query(1, l, r);
            printf("%s\n", t.h1 == t.h2 ? "Yes" : "No");
        }
    }
    
    return 0;
}

 

参考资料

  Editorial - Daiwa Securities Co. Ltd. Programming Contest 2023(AtCoder Beginner Contest 331):https://atcoder.jp/contests/abc331/editorial/7876

posted @   onlyblues  阅读(62)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
Web Analytics
点击右上角即可分享
微信分享提示