哈希的过程,其实可以看作对一个串的单向加密过程,并且需要保证所加的密不能高概率重复(就像不能让隔壁老王轻易地用它家的钥匙打开你家门一样qwq),通过这种方式来替代一些很费时间的操作。
比如,最常见的,当然就是通过哈希数组来判断几个串是否相同(洛谷p3370)。此处的操作呢,很简单,就是对于每个串,我们通过一个固定的转换方式,将相同的串使其的“密”一定相同,不同的串 尽量 不同。
此处有人指出:那难道不能先比对字符串长度,然后比对ASCLL码之和吗?事实上显然是不行的(比如ab和ba,并不是同一个串,但是如是做却会让其认为是qwq)。这种情况就叫做hash冲突,并且在如此的单向加密哈希中,hash冲突的情况在所难免(bzoj就有这种让你给出一组样例,使得一段哈希代码冲突的题,读者可以尝试尝试)。
而我们此处介绍的,即是最常见的一种哈希:进制哈希。进制哈希的核心便是给出一个固定进制k,将一个串的每一个元素看做一个进制位上的数字,所以这个串就可以看做一个k进制的数,那么这个数就是这个串的哈希值;则我们通过比对每个串的的哈希值,即可判断两个串是否相同
下面上代码(洛谷P3370):
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
#define AA 1000007
typedef unsigned long long ull;
int n;
char s[AA];
ull a[AA];
ull k=131,prime=233317;//k 与 mod应该为互质的两个数,prime 是一个大质数
ull mod=212370440130137957ll;
ull Hash(char s[]){
int l=strlen(s);
ull ans=0;
for(int i=0;i<l;i++){
ans=(ans*k+(ull)s[i])%mod+prime;
}
return ans;
}
int main() {
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%s",s);
a[i]=Hash(s);
}
sort(a+1,a+1+n);
int ans=0;
for(int i=1;i<n;i++){
if(a[i]==a[i+1]){
ans++;
}
}
printf("%d",n-ans);
return 0;
}
哈希还可以判断两个串s1,s2,求s1在s2中出现了多少次
例题:POJ3461
代码如下:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<string>
using namespace std;
unsigned long long pow[1000005];
unsigned long long sum [1000005];
string s1;
string s2;
int main(){
int T,ans = 0;
int b = 31;
pow[0] = 1;
for(int i = 1; i <= 1000000; i++){
pow[i] = pow[i-1] * b;
}
cin >> T;
while(T--){
ans = 0;
cin >> s1;
getchar();
cin >> s2;
int l1 = s1.size();
int l2 = s2.size();
sum[l2] = 0;
for(int i = l2 - 1; i >= 0; i--){
sum[i] = sum[i+1] * b + (unsigned long long)(s2[i] - 'A' + 1);
}
unsigned long long s = 0;
for(int j = l1 - 1; j >= 0; j--){
s = s * b + (unsigned long long)(s1[j] - 'A' + 1);
}
for(int i = 0; i <= l2 - l1; i++){
if(s == sum[i] - sum[i + l1] * pow[l1]) ++ans;
}
cout << ans << endl;
}
return 0;
}