本文哈希数组均为 1-index,原始字符串均为 0-index。
哈希值类
typedef unsigned long long ull;
ull bs=13131,bspw[MAXN];inline void init_bspw(){bspw[0]=1;for(int i=1;i<MAXN;i++)bspw[i]=bspw[i-1]*bs;
}struct HashNode{ull val;int len;HashNode(){}HashNode(ull a,int b):val(a),len(b){}
};
inline HashNode operator + (const HashNode& a,const HashNode& b){HashNode res;res.val=a.val*bspw[b.len]+b.val;res.len=a.len+b.len;return res;
}
inline HashNode operator - (const HashNode& a,const HashNode& b){//b is prefix of aHashNode res;res.val=a.val-b.val*bspw[a.len-b.len];res.len=a.len-b.len;return res;
}
inline bool operator == (const HashNode& a,const HashNode& b){return (a.len==b.len) && (a.val==b.val);
}
静态字符串哈希
class HashString{
private:HashNode hs[MAXN];
public:inline void build(const string& str){int len=str.size();hs[0]=HashNode(0,0);for(int i=1;i<=len;i++)hs[i]=HashNode(str[i-1]-'a'+1,1);for(int i=1;i<=len;i++)hs[i]=hs[i-1]+hs[i];}inline HashNode query(const int& l,const int& r) const{return hs[r]-hs[l-1];}
};
动态字符串哈希(线段树维护字符串哈希)
\(O(\log n)\) 单点修改,\(O(\log n)\) 区间查询。
通常不会有区间直接修改的需求,因为这样输入复杂度就出问题了(
#define getmid int mid=(l+r)/2
#define lch (pos<<1)
#define rch (pos<<1|1)class HashSegTree{
private:HashNode t[MAXN<<2];inline void pushup(int pos){t[pos]=t[lch]+t[rch];}
public:void build(int pos,int l,int r,const string& str){if(l==r){t[pos]=HashNode(str[l-1]-'a'+1,1);return;}getmid;build(lch,l,mid,str);build(rch,mid+1,r,str);pushup(pos);}void update(int pos,int l,int r,int aim,char ch){if(l==r){t[pos]=HashNode(ch-'a'+1,1);return;}getmid;if(aim<=mid) update(lch,l,mid,aim,ch);else update(rch,mid+1,r,aim,ch);pushup(pos);}HashNode query(int pos,int l,int r,int ll,int rr){if(ll<=l && r<=rr){return t[pos];}getmid;HashNode res(0,0);if(ll<=mid) res=res+query(lch,l,mid,ll,rr);if(mid<rr) res=res+query(rch,mid+1,r,ll,rr);return res;}
};
线段树维护正反串哈希
仍然是 \(O(\log n)\) 单点修改,\(O(\log n)\) 区间查询。
通常用于查询字符串的某个子串是不是回文串。
模板题
#include<bits/stdc++.h>
#define getmid int mid=(l+r)/2
#define lch (pos<<1)
#define rch (pos<<1|1)
using namespace std;
typedef unsigned long long ull;
const int MAXN=1e6+5;
ull bs=13131,bspw[MAXN];struct TwinHashNode{ull fwd,bwd;int len;TwinHashNode(){}TwinHashNode(ull a,ull b,int c):fwd(a),bwd(b),len(c){}
};
inline TwinHashNode operator + (const TwinHashNode& a,const TwinHashNode& b){TwinHashNode res;res.fwd=a.fwd*bspw[b.len]+b.fwd;res.bwd=b.bwd*bspw[a.len]+a.bwd;res.len=a.len+b.len;return res;
}class HashSegTree{
private:TwinHashNode t[MAXN<<2];inline void pushup(int pos){t[pos]=t[lch]+t[rch];}
public:void build(int pos,int l,int r,const string& str){if(l==r){t[pos]=TwinHashNode(str[l-1]-'a'+1,str[l-1]-'a'+1,1);return;}getmid;build(lch,l,mid,str);build(rch,mid+1,r,str);pushup(pos);}void update(int pos,int l,int r,int aim,char ch){if(l==r){t[pos]=TwinHashNode(ch-'a'+1,ch-'a'+1,1);return;}getmid;if(aim<=mid) update(lch,l,mid,aim,ch);else update(rch,mid+1,r,aim,ch);pushup(pos);}TwinHashNode query(int pos,int l,int r,int ll,int rr){if(ll<=l && r<=rr){return t[pos];}getmid;TwinHashNode res(0,0,0);if(ll<=mid) res=res+query(lch,l,mid,ll,rr);if(mid<rr) res=res+query(rch,mid+1,r,ll,rr);return res;}
}text;int n,q;
string str;int main(){ios::sync_with_stdio(false);cin>>n>>q>>str;n=str.size();bspw[0]=1;for(int i=1;i<=n;i++)bspw[i]=bspw[i-1]*bs;text.build(1,1,n,str);int opt,l,r,x;char c;while(q--){cin>>opt;if(opt==1){cin>>x>>c;text.update(1,1,n,x,c);}else{cin>>l>>r;auto res=text.query(1,1,n,l,r);cout<<(res.fwd==res.bwd?"Yes":"No")<<endl;}}return 0;
}