定义
- 将字符串映射为整数的函数f,我们称为字符串哈希函数
hash的目的
- 将元素分布稀疏的空间映射到元素分布相对密集的新空间。但是,原空间的不同元素可能映射到新空间的相同元素,这就是哈希冲突。我们希望哈希冲突的概率尽可能地小。
性质
- hash值不同,两个字符串一定不相同;
- 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值。
-
字符串哈希的应用
- 字符串匹配;
- 允许 k 次失配的字符串匹配;
- 最长回文子串;
- 最长公共子字符串;
- 确定字符串中不同子字符串的数量。
- 这里只字符串匹配为示例,并给出代码
字符串匹配
例题
给定一个长度为 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;
}