【未】#创作计划# 字符串哈希(基础篇)
2025-10-27 23:29:32
发布于:广东
嗯。
字符串哈希太好用了你知道吗。
由于一些基础字符串哈希算法在提高级别字符串算法已经不适用(例如字符串拼接,只能返回哈希值,不适合用于提高算法),本创作计划将分为“基础篇”与“提高篇”。
如果你对基础字符串哈希算法十分了解,请前往提高篇。
字符串哈希是一种简单、用处广泛的字符串算法,可以处理大部分字符串问题。
首先我们要知道字符串哈希的原理是什么。
其实就是把一个字符串通过乘法压缩成一个数值。这个数值就叫哈希值。
例如定义 为乘数,长度为 的字符串 就可以压缩成 。
这样我们就可以递推出 所有前缀的哈希值:。
这样,每个字符串都有一个对应的哈希值,不同的串哈希值也不同。
但这样数值是指数级增长的,到了后面 unsigned long long 都存不下,怎么办?
我们可以对哈希值对一个大数取模,这样虽然可能有两个不同字符串的哈希值相同,但错误率极小,如果模数是 ,则两个不同的随机字符串哈希值相同的概率为 (一般单模哈希的 不能取太小,因为根据生日悖论, 个随机字符串就很大概率出现一对哈希值相同的)。特别地,可以使用 unsigned long long 自然溢出,这可以看做模数 。
求一个字符串哈希代码如下(自然溢出,乘数 ):
ull get_hash(string s, int n){
ull ans = 0;
for(int i = 1; i <= n; i++){
ans = ans * mul + s[i];
}
return ans;
}
时间复杂度:。
字符串哈希可以干什么呢?你可以看看下面的内容。(吓哭了)
判断两字符串是否相等
题目
给定两个长度为 的字符串 ,判断这两个字符串是否相等。
即判断是否对于所有 ,满足 。
这个简单,判断哈希值是否相等即可。
#include <iostream>
#include <cstdio>
#define ull unsigned long long
using namespace std;
const ull mul = 131;
string s1, s2;
int n;
ull get_hash(string s, int n){
ull ans = 0;
for(int i = 1; i <= n; i++){
ans = ans * mul + s[i];
}
return ans;
}
bool equal(ull hsh1, ull hsh2){
return (hsh1 == hsh2);
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin >> n;
cin >> s1 >> s2;
s1 = ' ' + s1;
s2 = ' ' + s2;
cout << equal(get_hash(s1, n), get_hash(s2, n));
return 0;
}
求哈希值时间复杂度:。
判断相等时间复杂度:。
字符串拼接
题目
给定两个字符串 ,请输出他们的哈希值和拼接后的哈希值。
假设需要拼接两个字符串 ,它们的长度分别为 。
则它们的哈希值分别为 ,。
假设拼接后的字符串 。则有哈希值 。
预处理 即可。
#include <iostream>
#include <cstdio>
#define ull unsigned long long
using namespace std;
const ull mul = 131;
ull ksm[200005];
string s1, s2;
int n, m;
ull get_hash(string s, int n){
ull ans = 0;
for(int i = 1; i < s.size(); i++){
ans = ans * mul + s[i];
}
return ans;
}
ull merge(ull hsh1, ull hsh2, ull size1, ull size2){
return (hsh1 * ksm[size2]) + (hsh2);
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
ksm[0] = 1;
for(int i = 1; i <= 200000; i++){
ksm[i] = ksm[i - 1] * mul;
}
cin >> n >> m;
cin >> s1 >> s2;
s1 = ' ' + s1;
s2 = ' ' + s2;
cout << get_hash(s1, n) << ' ' << get_hash(s2, m) << ' ' << merge(get_hash(s1, n), get_hash(s2, m), n, m);
return 0;
}
预处理,求哈希值时间复杂度:。
拼接时间复杂度:。
截取字符串一个子串
题目
给定一个字符串 ,给出 次询问,首先输出 的哈希值,然后输出每次询问查找的子串的哈希值。
我们可以按照上面的方法,先预处理出这个字符串的前缀哈希值。
下面的代码就是记录一个字符串的前缀哈希值:
#include <iostream>
#include <cstdio>
#define ull unsigned long long
using namespace std;
const ull mul = 131;
string s;
int n;
ull HASH[1000005];
int main(){
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin >> n;
cin >> s;
s = ' ' + s;
for(int i = 1; i <= n; i++){
HASH[i] = HASH[i - 1] * mul + s[i];
}
return 0;
}
记要截取的子串为 。
则 。
#include <iostream>
#include <cstdio>
#define ull unsigned long long
using namespace std;
const ull mul = 131;
string s;
int n;
ull HASH[1000005];
ull ksm[1000005];
ull substr(int l, int len){
return HASH[l + len - 1] - ksm[len] * HASH[l - 1];
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin >> n;
cin >> s;
s = ' ' + s;
ksm[0] = 1;
for(int i = 1; i <= n; i++){
HASH[i] = HASH[i - 1] * mul + s[i];
ksm[i] = ksm[i - 1] * mul;
}
cout << HASH[n] << '\n';
int q;
cin >> q;
while(q--){
int l, len;
cin >> l >> len;
cout << substr(l, len) << '\n';
}
return 0;
}
预处理时间复杂度:
查询时间复杂度:
全部评论 6
你说我是不是可以分成上下篇讲水两个创作计划(((
5天前 来自 广东
2qp
6天前 来自 重庆
1d
6天前 来自 广东
1捉(
6天前 来自 重庆
0童哥这么晚还在肝OI吗?



6天前 来自 重庆
0瞎写一波水个创作计划(
6天前 来自 广东
1
d
4天前 来自 广东
0介绍一下用字符串哈希+<数据删除>(kachang)过KMP板子,不然狂踩
5天前 来自 浙江
0会的兄弟会的
5天前 来自 广东
01e6也不卡常吧
5天前 来自 广东
0border有点难找
5天前 来自 广东
0
d
6天前 来自 重庆
0



















有帮助,赞一个