一,定义
字符串哈希其实就是把一段字符串转化成一个数字。
在进行字符串匹配时不需要再 O (n)匹配字符串本身,而只需要匹配两个字符串的哈希值就好了O(1)
二,实现
h
a
s
h
(
s
)
=
Σ
i
=
0
n
s
[
i
]
∗
p
n
−
i
%
m
o
d
hash(s)=\Sigma_{i=0}^{n}s[i]*p^{n-i}\ \%mod
hash(s)=Σi=0ns[i]∗pn−i %mod
其中,n为字符串长度,s为该字符串,
p为基数,根据题目要求选择:ASCLL选择128,小写or大写字符可以采用26
哎!这时候就有聪明的同学发现了一个问题:
*采一朵花送给妈妈,妈妈,妈妈*
*作业写完了没!!!*
当我们算出哈希值并且mod完之后,想要写入时
这个位置已经被用过了
例如:
114%14=2
128%14=2
这就非常难受了
对此,我们有两种解决方法
一,开放寻址法
遇到冲突时,我们通过一定策略向后或者向前寻找空的位置并填入
二,链表法
如果遇到冲突,我们可以在当前节点后跟一个节点,形成一个链表,
但都太麻烦了,不想写,实际过程中我们可以尽量mod大质数来避免哈希冲突
例如:1e9+7,998244353…
下面来看代码:
计算哈希值
#define ll int
const ll mod=998244353;
ll hashp(string s){
long long ans=0;
for(int i=0;i<s.size();i++){
ans=(ans*128%mod+1ll*s[i])%mod;
}
return ans%mod;
}
求字串的哈希值:
这里我们采用前缀和思想
ll getnum(int l, int r) {
return (hash[r]-hash[l-1]*pw[r-l+1]);//对齐之后以便于相减
}
//=============================
for (int len = 1; len <= n; len++) {
hash[len]=hash[len - 1]*26+s[len - 1];
}//
pw[0] = 1;
for (int i = 1; i <= 100005; i++) {
pw[i] = pw[i - 1] * 26;
}//初始化
cout<<get(l,r);
三,例题
1.模板例题
题目描述
对于一个字符串,把字符串的每个字符转化成其对应的数字,可以得到一个128进制的数(每一位上的数字都在0~127中),再将这个128进制的数转成对应的十进制,就可以得到对应的哈希值。
输入一个字符串,请输出其哈希值对998244353取余的结果。
输入描述
输入一行,包含一个字符串。
输出描述
输出其哈希值对998244353取余的结果。
输入样例1
apple
输出样例1
320616011
输入样例2
nihao!chilema?
输出样例2
979475980
数据范围
字符串长度不超过100000
AC代码:
#include<bits/stdc++.h>
using namespace std;
#define ll int
const ll mod=998244353;
ll hashp(string s){
long long an=0;
for(int i=0;i<s.size();i++){
an=(an*128%mod+1ll*s[i])%mod;
}
return an%mod;
}
signed main(){
string s;
getline(cin,s);
cout<<hashp(s)<<endl;
return 0;
}
二。进阶提升
这里我们使用map来处理密码表之间的对应关系
AC代码:
#include <bits/stdc++.h>
using namespace std;
#define int ll
#define ll long long
const ll mod = 998244353;
ll pw[100005];
unordered_map<char, char> mp;
ll hash_mi[100005], hash_ming[100005];
ll hashp(string s) {
long long an = 0;
for (int i = 0; i < s.size(); i++) {
an = (an * 128 + 1ll * s[i]);
}
return an % mod;
}
ll getnum(int l, int r) {
return (hash_mi[r] - hash_mi[l - 1] * pw[r - l + 1]);
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int T;
cin >> T;
pw[0] = 1;
for (int i = 1; i <= 100005; i++) {
pw[i] = pw[i - 1] * 26;
}
for (int u = 1; u <= T; u++) {
for (int i = 'a'; i <= 'z'; i++) {
char a;
cin >> a;
mp[a] = i;
}
string s;
cin >> s;
int n = s.size();
for (int len = 1; len <= n; len++) {
hash_mi[len] = hash_mi[len - 1] * 26 + s[len - 1];
hash_ming[len] = hash_ming[len - 1] * 26 + mp[s[len - 1]];
}
int ans = 0;
for (int k = (n + 1) / 2; k <= n; k++) {
if (hash_ming[n - k] == getnum(k + 1, n)) {
ans = k;
break;
}
}
for (int i = 0; i < ans; i++) {
cout << s[i];
}
for (int i = 0; i < ans; i++) {
cout << mp[s[i]];
}
cout << endl;
}
return 0;
}