目录
1.定义:
字符串哈希是将一段字符串换为一个数,当查询是否包含某个字符串时,可以每次O(1)复杂度进行比对。比如:abc对应1,acd对应2,sdf对应3。(具体数值由计算决定)
2.字符串哈希值计算:
从第一个字符开始,逐次向后计算。计算公式:
hash[i] = hash[i-1] * base + (ull)(s[i - 1] - ‘A’ + 1);
计算的本质就是b进制数的计算,b进制数位上的数字是字符对应的数字。
从第i个字符到第j个字符的值的计算:
Hash(S')=Hash( j ) - H( i ) * b^(j-i)
3.字符串哈希的使用:
先计算两个字符串的字符串哈希值,然后将短字符串的哈希值,分别和长字符串种等长的字符串哈希值对比,如果相同,则包含该字符串(重复概率很小,可以使用双哈希进行进一步验证)。
例子:
小 Z 同学没天都喜欢斤斤计较,今天他又跟字符串杠起来了。
他看到了两个字符串 s1 s2 ,他想知道 S1 在 S2 中出现了多少次。
现在给出两个串 S 1,S 2(只有大写字母),求 S 1 在 S 2 中出现了多少次。
数据范围字符串长度len, 1<len(s1)<len(s2)<10^6
字符取值 大写字母 和 小写字母
实现:
这段代码实现了字符串哈希,并借此实现了字符串匹配搜索,即在一个字符串中找到另一个字符串出现的次数。
代码主要有以下几个部分:
1. 头文件和常量定义
#include <iostream>
#include <cstdio>
#include <string>
using namespace std;
typedef unsigned long long ull;
const int base = 131;
const int maxn = 1e6 + 5;
头文件中包含了输入输出流和字符串类库,const定义了两个常用到的变量,base是计算哈希值的权值,maxn是字符串最大长度。
2. 函数初始化
void init() {
power[0] = 1;
for (int i = 1; i <= 10002; i++)//预处理base^n
power[i] = power[i - 1] * base;
}
该函数用于预处理出base的n次方,以便在计算哈希值的时候直接用power[n]来代替循环n次计算。
3. 主函数
int main() {
init();
scanf("%s", &sTemp1);
scanf("%s", &sTemp2);
s1 = sTemp1;
s2 = sTemp2;
//强烈建议这样读取字符串,节省时间
int len1 = s1.size();
int len2 = s2.size();
for (int i = 1; i <= len1; ++i)
hash1 = hash1 * base + (ull)(s1[i - 1] - 'A' + 1);
hash2[0] = (s2[0] - 'A' + 1);
for (int i = 1; i <= len2; ++i)
hash2[i] = hash2[i - 1] * base + (ull)(s2[i - 1] - 'A' + 1);
for (int i = 0; i <= len2 - len1; ++i) {
ull hash = hash2[i + len1] - hash2[i] * power[len1];
if (hash == hash1)
ans++;
}
printf("%d\n", ans);
return 0;
}
在主函数中,首先调用init函数进行初始化,然后输入两个字符串并存入s1和s2中,用size()函数获取两个字符串的长度。之后通过循环计算出s1和s2的哈希值,并且在s2中循环匹配s1的哈希值是否出现。最后输出匹配到的次数。
在哈希计算过程中,对于每个字符,先将其转为数字,再将数字作为加数乘以权值base,最后累加到哈希值中。因此针对每个位置,哈希值的计算公式为:
hash = hash * base + (ull)(s[i] - 'A' + 1);//这里-'A'+1的作用是将字符转化为数字
当要在字符串中匹配某个子串时,可以先计算子串的哈希值,再用滑动窗口的方法,在主串中循环计算哈希值进行匹配。
在这段代码中,由于字符串长度较长,为了避免函数调用带来额外的开销,使用scanf和预处理输入的方式进行字符串读取。