本文哈希数组均为 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 a
HashNode 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;
}
标签:log,int,线段,哈希,字符串,bspw,define
From: https://www.cnblogs.com/SkyNet-PKN/p/18414948