1. 程式人生 > >字串雜湊(進位制雜湊)

字串雜湊(進位制雜湊)

雜湊的過程,其實可以看作對一個串的單向加密過程,並且需要保證所加的密不能高概率重複(就像不能讓隔壁老王輕易地用它家的鑰匙開啟你家門一樣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中出現了多少次

設H(c,k)為前k個字元構成的字串的雜湊值

遞推式:H(c,k+1)= H(c,k) * b + s[k+1]

計算字串c從位置k+1開始的長度為n的子串的雜湊值,可得遞推式:

H(c') = H(c, k + n) - H(c,k) * b^n

程式碼如下:

#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;
}