基本数据结构—Hash哈希
理論概念
這玩意一直都是個好東西,但是我總覺得玄學的一批。今天借著專題學習的勁頭,把Hash好好梳理一下。
定義/作用
哈希這東西應該都不陌生。將復雜的信息映射到一個容易維護的值域之內。那么Hash函數就有點類似于一個映射關系。通過這個函數來產生一個關鍵值(Key),通過關鍵值與值(value)的對應關系,制作一個對應表。即哈希表(Hash table)。他可以實現通過Key快速的查找Value。
那么其最大作用也就顯而易見。查重。也就是說,查找當前的值是否已經存在。難點在于,如何去產生這個對應關系?簡單的映射關系是有相當的可能產生“兩組不同的值生成同一個關鍵值”的錯誤(我們稱其為哈希沖突)。如果哈希函數的設計太水,那么這個哈希沖突也就非常容易產生了。
整數(int)哈希舉例
引用了一個對整數數組生成Hash的方法,這里只做舉例。在本例中,不關心數組中數的序列問題。即只考慮數組內值不同為不同狀態,如果值相同但順序不同,我們仍認為他是同一狀態。(會產生相同的關鍵值)
對于一組數,其乘積與其累加之和與任意較大質數P取模。
//這里是針對于某一個題目所引入的,所以具體原因會在下方的題目展示中闡釋。
字符串哈希
定義與技巧
常用的字符串哈希分兩類(沒錯,我覺得該分兩類的!)
自然溢出式Hash與多重Hash。一般來講,前者雖然發生哈希沖突的幾率非常小,但總會被刁鉆(誒嘿嘿)的出題人卡幾個數據。是的,他們是具備能卡你自然溢出單哈希的能力的。所以穩妥起見,后者運用頻率偏高一些。
什么是字符串哈希?就是把一個任意長度的字符串映射成一個非負整數,并且其沖突概率幾乎為0
把字符串看作P進制數,并分配一個大于0的數值,代表每種字符。啥意思啊?比如a對應1,b對應2,c對應3。取一個固定的值M,求出該P進制數對于M的余數,作為該字符串的哈希值。一般來說,P取131或者13331,這樣沖突幾率就極低。一般M取264,也就是一個自然溢出的unsigned long long。就讓你溢出,取代低效的取模運算。
這也就是第一類Hash。為了避免被卡,我們多取一些適當的P與M。(例如大質數),進行多組Hash運算。如果結果都相同,我們就認定這是一個相等的字符串。
性質
對沒錯!我覺得這是性質!!
有了以下兩種性質,我們便可以做到在O(N)的時間內預處理所有前綴Hash值,并在O(1)的時間內完成查詢任意字串的哈希值。
一、
對于任意新字符串S+C,他的Hash值就是
?H(S+C)=(H(S)*P+Value[C])mod M?
為什么?我們來進行一個簡單的模擬。
例如,S="abc",C=“d”
S表示為P進制的數:1 2 3
H(S)=1*P2+2*P+3
H(S+C)=1*P3+2*P2+3*P+4=H(S)*P+4
這里可以把*P的操作視為在P進制下的左移運算。Value[c]是我們為c選定的代表數值。
二、
如果已知字符串S的Hash值為H(S),字符串S+T的Hash值為H(S+T),那么字符串T的Hash值
H(T)=(H(S+T)-H(S)*Plength(T)) mod M
那么我們繼續之前的模擬。
例如,S="abc",C=“d”,T=“xyz”
S表示為P進制的數:1 2 3
S+T的P進制數為:1 2 3 24 25 26
H(S)=1*P2+2*P+3
H(S+T)=1*P5+2*P4+3*P3+24*P2+25*P+26
S在P進制下左移length(T)位:1 2 3 0 0 0?
兩者相減就是T表示為P進制數:24 25 26
H(T)=H(S+T)-(1*P2+2*P+3)*P3=24*P2+25*P+26
在題目中的Show Time
Snowflake Snow Snowflakes? POJ3349
等待補充
兔子與兔子 CH1401
由于目標題庫被查表…等待恢復。
好文章 2015模擬題By nodgd
因為這是一個專題博文,所以我不會去仔細深究他沒有涉及到哈希的原理。
在這道題中,我們可以枚舉每一個長度為m的子串。那么一共會有n-m+1個長度為m的子串。我們將他們的哈希值分別取出來,然后再進行比對。如果相等,則忽略,否則ans++
我聽說這道題用自然溢出哈希是翻車了的。故我貼這個題的目的,是想貼一下雙哈希的大概樣子。
#include<bits/stdc++.h> #define N 200009 using namespace std; char a[N]; long long ans, n, m,hs2[N], len1[N], len2[N], hs1[N],p1=131,p2=13331,mod1=1000000009,mod2=1000000007; struct hah{long long h1, h2; }hash[N]; long long cmp(hah x, hah y){if (x.h1 == y.h1) return x.h2 < y.h2; return x.h1 < y.h1; } void prepare(){len1[0] = 1, len2[0] = 1;for (int i = 1; i <= n; i++){hs2[i] = (hs2[i - 1] * p2 + (a[i] - 'a' + 1)) % mod2;//性質1 hs1[i] = (hs1[i - 1] * p1 + (a[i] - 'a' + 1)) % mod1;len1[i] = (len1[i - 1] * p1) % mod1, len2[i] = (len2[i - 1] * p2) % mod2;//預處理了p的前綴和 次方 } } void gethash(long long l, long long r, long long oi){hash[oi].h2 = (hs1[r] - hs1[l - 1] * len1[r - l + 1] % mod1 + mod1) % mod1;//性質2 hash[oi].h1 = (hs2[r] - hs2[l - 1] * len2[r - l + 1] % mod2 + mod2) % mod2; } int main(){cin>>n>>m;cin >> a + 1;prepare();for (int i = 1; i <= n - m + 1; i++)gethash(i, i + m - 1, i);//枚舉每個子串并取其哈希值 sort(hash + 1, hash + n - m + 2, cmp);for (int i = 1; i <= n - m + 1; i++)if (hash[i].h2 == hash[i + 1].h2 && hash[i].h1 == hash[i + 1].h1) continue;else ans++;cout<<ans; }轉載于:https://www.cnblogs.com/Uninstalllingyi/p/11191045.html
總結
以上是生活随笔為你收集整理的基本数据结构—Hash哈希的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: golang 学习 (八)协程
- 下一篇: 不用中间变量交换a和b的值?