【基本算法】字符串哈希

定义

  • 将字符串映射为整数的函数f,我们称为字符串哈希函数

hash的目的

  • 将元素分布稀疏的空间映射到元素分布相对密集的新空间。但是,原空间的不同元素可能映射到新空间的相同元素,这就是哈希冲突。我们希望哈希冲突的概率尽可能地小。

性质

  1. hash值不同,两个字符串一定不相同;
  2. hash值相同,两个字符串不一定相同,我们将hash值一样但是字符串不一样的情况为哈希冲突。

hash准确率

  • 通常我们采用的是多项式 hash 的方法,对于一个长度为 l 的字符串 s 来说,我们可以这样定义多项式 hash 函数:在这里插入图片描述
  • 这种定义方法相当于将字符串看成是b进制的数。例如字符串xyz,它的值为x * b^2 + y * b^1 + z * b^0,也即:x * b^2 + y * b + z
  • 式中的M和哈希碰撞的概率有关。一般来说,M、b都会选择一个大于最大字符的ASCII值的素数。
  • 哈希碰撞的概率

    在这里插入图片描述

多次访问字符串哈希

  • 每次对字符串求哈希值的时间复杂度为O(n),与暴力匹配没有很大的区别,如果多次访问一个字符串的不同字串,则效率十分低下。
  • 因此,我们可以预处理出字符串每个前缀的哈希值,将hash值看成一个b进制的数对M取模的结果,这样就可以快速求出每个子串的hash值。
  • 在这里插入图片描述

字符串哈希的应用

  1. 字符串匹配;
  2. 允许 k 次失配的字符串匹配;
  3. 最长回文子串;
  4. 最长公共子字符串;
  5. 确定字符串中不同子字符串的数量。
  • 这里只字符串匹配为示例,并给出代码

字符串匹配

例题

给定一个长度为 n 的字符串,再给定 m 个询问,每个询问包含四个整数 l1,r1,l2,r2,请你判断 [l1,r1] 和 [l2,r2] 这两个区间所包含的字符串子串是否完全相同,相同输出“Yes”,否则输出“No”。
字符串中只包含大小写英文字母和数字。

输入输出

  • 输入:
    8 3
    aabbaabb
    1 3 5 7
    1 3 6 8
    1 2 1 2
  • 输出:
    Yes
    No
    Yes

code

#include <iostream>
#include <math.h>

using namespace std;

const int N = 100010;
const int P = 131;//P代表进制
char c[N];
unsigned long long h[N],p[N];//h[i]表示c[1-i]的前缀hash值,p[i]表示基数,例如10^1、10^2等
void Deal(int n)
{
    h[1] = c[1];
    p[1] = P;
    for (int i = 2; i <= n; i++) {
        p[i] = p[i - 1] * P;
        h[i] = h[i - 1] * P + c[i];
    }
}
unsigned long long Count(int l, int r)
{
	//与前文分析一致
    return h[r] - h[l - 1] * p[r - l + 1];
}
int main()
{
    int n, m;
    cin >> n >> m;
    cin >> c + 1;
    Deal(n);
    int l1, r1, l2, r2;
    while (m--) {
        cin >> l1 >> r1 >> l2 >> r2;
        if (Count(l1, r1) == Count(l2, r2)) cout << "Yes" << endl;
        else cout << "No" << endl;
    }
    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值