哈希的过程,其实可以看作对一个串的单向加密过程,并且需要保证所加的密不能高概率重复(就像不能让隔壁老王轻易地用它家的钥匙打开你家门一样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;
} 

 

10-06 16:38