字符串哈希

hash函数

设字符集大小为 B B B,将所有字符编号为 0 , 1 , . . . , B − 1 0,1,...,B-1 0,1,...,B1,则一个串可以看做一个 B B B进制数,以 B B B进制展开计算一个长 m m m的串 t t t h a s h hash hash值, P P P是一个大质数,减少碰撞次数

H a s h ( t ) = ∑ i = 0 m − 1 t i ⋅ B m − i + 1   m o d   P Hash(t)=\sum_{i=0}^{m-1}{ t_i·B^{m-i+1}~mod~P} Hash(t)=i=0m1tiBmi+1 mod P

单模数哈希

ull base=131;
int prime=233317;
ull mod=212370440130137957LL;

ull hash(char *s){
    int len=strlen(s);
    ull ans=0;
    for(int i=0;i<len;i++)
    	ans=(ans*base+(ull)s[i])%mod+prime;
    return ans;
}

自然溢出哈希

根据 u s i g n e d    l o n g    l o n g usigned~ ~long~~ long usigned  long  long自然溢出的性质优化(溢出自动取模)

ull base=131;

ull hash(char *s){
    int len=strlen(s);
    ull ans=0;
    for(int i=0;i<len;i++)
    	ans=ans*base+(ull)s[i];
    return ans;
}

双哈希

更安全的做法是双哈希,翻车几率更低

ull base=131;
ull mod1=19260817,mod2=19660813;

ull hash1(char *s){
    int len=strlen(s);
    ull ans=0;
    for(int i=0;i<len;i++)
        ans=(ans*base+(ull)s[i])%mod1;
    return ans;
}

ull hash2(char *s){
    int len=strlen(s);
    ull ans=0;
    for(int i=0;i<len;i++)
        ans=(ans*base+(ull)s[i])%mod2;
    return ans;
}
hash函数的滚动性质

H ( s , l , r ) H(s,l,r) H(s,l,r)为串 s s s的子串 s l s l + 1 . . . s r s_ls_{l+1}...s_r slsl+1...sr h a s h hash hash值,则:

H ( s , l + 1 , r + 1 ) = ( H ( s , l , r ) − s l B r − l ) ⋅ B + S r + 1 H(s,l+1,r+1)=(H(s,l,r)-s_lB^{r-l})·B+S_{r+1} H(s,l+1,r+1)=(H(s,l,r)slBrl)B+Sr+1
在这里插入图片描述
例如找串 s s s中是否包含子串 t t t

利用滚动性质,先 O ( m ) O(m) O(m)求出模式串 t t t h a s h hash hash值,然后 O ( n ) O(n) O(n)扫一遍文本串 s s s所有长 m m m的子串的 h a s h hash hash值,就可以找出匹配

ull base=131;

int rabin_karp(char *t,char *s){
    int n=strlen(s),m=strlen(t);
    if(n<m) return -1;
    ull bn=1;
    for(int i=0;i<m-1;i++) bn=bn*base;
    ull key=0,cur=0;
    for(int i=0;i<m;i++){
        key=key*base+(t[i]-'a');
        cur=cur*base+(s[i]-'a');
    }
    for(int i=0;i<n-m+1;i++){
        if(cur==key) return i;
        cur=cur-bn*(s[i]-'a');
        cur=cur*base+(s[i+m]-'a');
    }
    return -1;
}
动态维护子串hash

区间的 h a s h hash hash是可合并的,即如果知道了 H ( s , l , m i d ) H(s,l,mid) H(s,l,mid) H ( s , m i d + 1 , r ) H(s,mid+1,r) H(s,mid+1,r),则:

H ( s , l , r ) = ( H ( s , l , m i d ) ∗ B r − m i d + H ( s , m i d + 1 , r ) )   m o d   P H(s,l,r)=(H(s,l,mid)*B^{r-mid}+H(s,mid+1,r))~mod~P H(s,l,r)=(H(s,l,mid)Brmid+H(s,mid+1,r)) mod P

问题引入

维护子串的 h a s h hash hash值,有两种操作:

  • 单点修改:可以将 s s s的某个字符修改为 c c c
  • 区间查询:查询 s s s的子串 s l s l + 1 . . . s r s_ls_{l+1}...s_r slsl+1...sr h a s h hash hash
ull base=131;

char s[maxn];
ull sgt[maxn*4],bn[maxn]; //bn[i]=base^i

void build(int i,int l,int r){
    if(l==r){
        sgt[i]=s[l]-'a';
        return;
    }
    int mid=(l+r)>>1,k=i<<1;
    build(k,l,mid);
    build(k|1,mid+1,r);
    sgt[i]=sgt[k]*bn[r-mid]+sgt[k|1];
}

ull query(int i,int l,int r,int x,int y){
    if(x<l || y<r) return 0;
    if(x<=l && y<=r) return sgt[i];
    int mid=(l+r)>>1,k=i<<1;
    int len=max(0,max(r,y)-max(mid+1,x)+1);
    return query(k,l,mid,x,y)*bn[len]+query(k|1,mid+1,r,x,y);
}

void modify(int i,int l,int r,int pos,char x){
    if(l==r){
        sgt[i]=x-'a';
        return;
    }
    int mid=(l+r)>>1,k=i<<1;
    if(pos<=mid) modify(k,l,mid,pos,x);
    else modify(k|1,mid+1,r,pos,x);
    sgt[i]=sgt[k]*bn[r-mid]+sgt[k|1];
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值