题意:给定一个字符串,(小于1e5), 有两种操作.
1. 将字符串某一位改成另一个字符。
2. 判断l,r区间内的字符串是不是回文串。
思路:因为字符串不停在变化,即使不变,求出每个子段是否回文也要n方。所以将这个字符串看作正反两个26进制的数,如果某一段是回文的,那他在正反两段中的哈希值应该相等。用树状数组区间求和。另外,由于正反串在源串中位置不同,要乘上到串末端距离,消除误差。
哈希看似很傻很暴力,写起来要注意的地方不少,需要很多巧妙的写法。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 100010;
typedef unsigned long long LL;
char t[maxn];
LL hsh[maxn];
LL tree[2][maxn];
void add(int poi,LL d,bool fg) {
while(poi<maxn) {
tree[fg][poi]+=d;
poi+=poi&-poi;
}
}
LL sum(int poi,bool fg) {
LL res = 0;
while(poi) {
res+=tree[fg][poi];
poi-=poi&-poi;
}
return res;
}
int lt = 0,q=0;
int main() {
hsh[0] = 1;
for(int i=1;i<maxn;i++) hsh[i] = hsh[i-1]*27;
while(~scanf("%s",t)){
lt = strlen(t);
memset(tree,0,sizeof(tree));
for(int i=0;i<lt;i++) {
add(i+1,(t[i]-'a')*hsh[i],0);
add(i+1,(t[lt-i-1]-'a')*hsh[i],1);
//cout<<sum(i+1,1)<<endl;
}
scanf("%d",&q);
while(q--) {
//cout<<t+1<<endl;
char ch[22];
scanf("%s",ch);
if(ch[0] == 'p') {
int l,r;
scanf("%d%d",&l,&r);
LL sum1 = hsh[lt-r]*(sum(r,0)-sum(l-1,0));
LL sum2 = hsh[l-1]*(sum(lt-l+1,1)-sum(lt-r,1));
//cout<<sum1<<" "<<sum2<<endl;
if(sum1 == sum2) puts("Yes");
else puts("No");
}
else {
int p;char chh[2];
scanf("%d%s",&p,chh);
int x = chh[0]-t[p-1];
add(p,x*hsh[p-1],0);
add(lt-p+1,x*hsh[lt-p],1);
t[p-1] = chh[0];
}
}
}
return 0;
}