ABC 331 F - Palindrome Query(字符串哈希,树状数组)
字符串哈希
[OI-Wiki](字符串哈希 - OI Wiki (oi-wiki.org))
分为两种哈希方式:以左为高位 和 以右为高位
如果只是快速查询每个字串的哈希值,用以左为高位比较简单,即
\[Hash[l...r]=Hash[1...r]-Hash[1...(l-1)]\times base^{r-l+1}
\]
但是如果有修改操作,需要将每一位的 Hash 值存到数据结构里(例如线段树、树状数组),那么采取以右为高位可以直接通过区间求和来获取 1...x 的Hash值。但是要注意求 [l,r] 的Hash值时需要再除以\(base^{l-1}\)
判断回文
将字符串正着哈希一遍,逆着哈希一遍,若两个Hash值相等,则回文。
具体实现可以开两个树状数组。
代码
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<queue>
#include<set>
#include<map>
#include<vector>
#include<iomanip>
#include<ctime>
#include<stack>
using namespace std;
inline int read(){
int x=0,f=1;char c=getchar();
while(!(c>='0'&&c<='9')) {if(c=='-') f=-1;c=getchar();}
while(c>='0'&&c<='9') {x=x*10+c-'0';c=getchar();}
return x*f;
}
const int maxn=1000005;
const long long mod=998244353;
int n,q;
char s[maxn];
long long d[maxn],d2[maxn],mi[maxn];
inline int lowbit(int x){
return x&(-x);
}
void add(int x,long long v){
for(int i=x;i<=n;i+=lowbit(i)) d[i]=(d[i]+v)%mod;
}
void add2(int x,long long v){
for(int i=x;i<=n;i+=lowbit(i)) d2[i]=(d2[i]+v)%mod;
}
long long query(int x){
long long res=0;
for(int i=x;i>=1;i-=lowbit(i)) res+=d[i];
return res%mod;
}
long long query2(int x){
long long res=0;
for(int i=x;i>=1;i-=lowbit(i)) res+=d2[i];
return res%mod;
}
long long has(int l,int r){
return (query(r)-query(l-1)+mod)%mod*mi[n-l]%mod;
}
long long has2(int l,int r){
return (query2(r)-query2(l-1)+mod)%mod*mi[n-l]%mod;
}
int main()
{
n=read(),q=read();
mi[0]=1;
for(int i=1;i<=n;i++){
mi[i]=mi[i-1]*27%mod;
}
for(int i=1;i<=n;i++){
s[i]=getchar();
while(s[i]<'a'||s[i]>'z') s[i]=getchar();
add(i,mi[i-1]*(s[i]-'a'+1)%mod);
add2(n-i+1,mi[n-i]*(s[i]-'a'+1)%mod);
}
while(q--){
int tp=read();
if(tp==1){
int x=read();
char c=getchar();
add(x,mi[x-1]*(c-s[x]+mod)%mod);
add2(n-x+1,mi[n-x]*(c-s[x]+mod)%mod);
s[x]=c;
}else{
int x=read(),y=read();
if(has(x,y)==has2(n-y+1,n-x+1)) puts("Yes");
else puts("No");
}
}
return 0;
}