字符串哈希

字符串哈希法是一个处理字符串的利器,可以简化很多字符串的问题。有时用这个方法比KMP更有优势。

什么是字符串前缀哈希

举个“栗子”就明白!

例如 : str = “ABCDEFG”

H(1) = "A"的哈希值

H(2) = "AB"的哈希值

H(3) = "ABC"的哈希值

``` ```

步骤

1.把字符串看成是一个p进制的数,把字符串中的每个字符看成是p进制数中的每一位;

2.把p进制的数转化成10进制的数 mod Q;(不进行取模操作的话得到的数字可能比较大,存储会不方便)

3.这样就可以把任何一个字符串映射到从0~Q-1之间的一个数。

例如:H(4) = "ABCD"的哈希值 = (1*p^{3} + 2*p^{2} + 3*p^{1} + 4*p^{0} )mod Q;

:取模通常取2^{64},如果我们用unsigned long long 来存储所有的h,就不需要取模运算了。溢出就相当于取模了,溢出就等价于模上2^{64} 

 

注:

①不能映射成0,通常映射成从1开始;

若H(1) = "A" = 0, 那么"AA"的哈希值也为0,可见会把不同的字符串映射为同一个数0,这样是不对的。

②字符串哈希假定RP足够好,不存在冲突。

应用

 

(求L到R的哈希值)

思路:

对于h[R]来说 R是低位,1是高位;对于h[L - 1]来说,L-1是低位,1是高位。

那么我们通过 h[L-1] * p^{^R-L+1},就可以使h[L-1]左移到与h[R]对齐。

得到公式: h[R] - h[L-1] * {\color{Blue} p^{R-L+1}} 

这样,预处理每一个前缀哈希值之后,就可以用O(1)的时间复杂度算出任意一个子段的哈希值了。

预处理前缀哈希值:h[i] = h[i-1]*p + str[i]; (str[i]表示第i位上的字母)

典例——AcWing 841.字符串哈希

#include <iostream>

using namespace std;

typedef unsigned long long ull;

const int N = 100010,P = 131; //P代表P进制,根据经验,这里通常取P = 131或13331

char str[N];
ull h[N],p[N];
//h[]代表前缀哈希值,即前i个字符的哈希值
//p[]代表p的几次方

ull get(int l,int r)
{
    return h[r] - h[l-1]*p[r-l+1];
}
int main()
{
    int n,m;
    scanf("%d%d%s",&n,&m,str + 1);
    p[0] = 1;
    for(int i = 1; i <= n; i ++ )
    {
        p[i] = p[i-1]*P;
        h[i] = h[i-1]*P + str[i];
    }
    while(m -- )
    {
        int l1,r1,l2,r2;
        cin >> l1 >> r1 >> l2 >> r2;
        if(get(l1,r1) == get(l2,r2)) puts("Yes");
        else puts("No");
    }
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值