基本数据结构—Hash哈希
理論概念
這玩意一直都是個(gè)好東西,但是我總覺得玄學(xué)的一批。今天借著專題學(xué)習(xí)的勁頭,把Hash好好梳理一下。
定義/作用
哈希這東西應(yīng)該都不陌生。將復(fù)雜的信息映射到一個(gè)容易維護(hù)的值域之內(nèi)。那么Hash函數(shù)就有點(diǎn)類似于一個(gè)映射關(guān)系。通過這個(gè)函數(shù)來產(chǎn)生一個(gè)關(guān)鍵值(Key),通過關(guān)鍵值與值(value)的對(duì)應(yīng)關(guān)系,制作一個(gè)對(duì)應(yīng)表。即哈希表(Hash table)。他可以實(shí)現(xiàn)通過Key快速的查找Value。
那么其最大作用也就顯而易見。查重。也就是說,查找當(dāng)前的值是否已經(jīng)存在。難點(diǎn)在于,如何去產(chǎn)生這個(gè)對(duì)應(yīng)關(guān)系?簡單的映射關(guān)系是有相當(dāng)?shù)目赡墚a(chǎn)生“兩組不同的值生成同一個(gè)關(guān)鍵值”的錯(cuò)誤(我們稱其為哈希沖突)。如果哈希函數(shù)的設(shè)計(jì)太水,那么這個(gè)哈希沖突也就非常容易產(chǎn)生了。
整數(shù)(int)哈希舉例
引用了一個(gè)對(duì)整數(shù)數(shù)組生成Hash的方法,這里只做舉例。在本例中,不關(guān)心數(shù)組中數(shù)的序列問題。即只考慮數(shù)組內(nèi)值不同為不同狀態(tài),如果值相同但順序不同,我們?nèi)哉J(rèn)為他是同一狀態(tài)。(會(huì)產(chǎn)生相同的關(guān)鍵值)
對(duì)于一組數(shù),其乘積與其累加之和與任意較大質(zhì)數(shù)P取模。
//這里是針對(duì)于某一個(gè)題目所引入的,所以具體原因會(huì)在下方的題目展示中闡釋。
字符串哈希
定義與技巧
常用的字符串哈希分兩類(沒錯(cuò),我覺得該分兩類的!)
自然溢出式Hash與多重Hash。一般來講,前者雖然發(fā)生哈希沖突的幾率非常小,但總會(huì)被刁鉆(誒嘿嘿)的出題人卡幾個(gè)數(shù)據(jù)。是的,他們是具備能卡你自然溢出單哈希的能力的。所以穩(wěn)妥起見,后者運(yùn)用頻率偏高一些。
什么是字符串哈希?就是把一個(gè)任意長度的字符串映射成一個(gè)非負(fù)整數(shù),并且其沖突概率幾乎為0
把字符串看作P進(jìn)制數(shù),并分配一個(gè)大于0的數(shù)值,代表每種字符。啥意思啊?比如a對(duì)應(yīng)1,b對(duì)應(yīng)2,c對(duì)應(yīng)3。取一個(gè)固定的值M,求出該P(yáng)進(jìn)制數(shù)對(duì)于M的余數(shù),作為該字符串的哈希值。一般來說,P取131或者13331,這樣沖突幾率就極低。一般M取264,也就是一個(gè)自然溢出的unsigned long long。就讓你溢出,取代低效的取模運(yùn)算。
這也就是第一類Hash。為了避免被卡,我們多取一些適當(dāng)?shù)腜與M。(例如大質(zhì)數(shù)),進(jìn)行多組Hash運(yùn)算。如果結(jié)果都相同,我們就認(rèn)定這是一個(gè)相等的字符串。
性質(zhì)
對(duì)沒錯(cuò)!我覺得這是性質(zhì)!!
有了以下兩種性質(zhì),我們便可以做到在O(N)的時(shí)間內(nèi)預(yù)處理所有前綴Hash值,并在O(1)的時(shí)間內(nèi)完成查詢?nèi)我庾执墓V怠?/p>
一、
對(duì)于任意新字符串S+C,他的Hash值就是
?H(S+C)=(H(S)*P+Value[C])mod M?
為什么?我們來進(jìn)行一個(gè)簡單的模擬。
例如,S="abc",C=“d”
S表示為P進(jìn)制的數(shù):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進(jìn)制下的左移運(yùn)算。Value[c]是我們?yōu)閏選定的代表數(shù)值。
二、
如果已知字符串S的Hash值為H(S),字符串S+T的Hash值為H(S+T),那么字符串T的Hash值
H(T)=(H(S+T)-H(S)*Plength(T)) mod M
那么我們繼續(xù)之前的模擬。
例如,S="abc",C=“d”,T=“xyz”
S表示為P進(jìn)制的數(shù):1 2 3
S+T的P進(jìn)制數(shù)為: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進(jìn)制下左移length(T)位:1 2 3 0 0 0?
兩者相減就是T表示為P進(jìn)制數(shù):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
等待補(bǔ)充
兔子與兔子 CH1401
由于目標(biāo)題庫被查表…等待恢復(fù)。
好文章 2015模擬題By nodgd
因?yàn)檫@是一個(gè)專題博文,所以我不會(huì)去仔細(xì)深究他沒有涉及到哈希的原理。
在這道題中,我們可以枚舉每一個(gè)長度為m的子串。那么一共會(huì)有n-m+1個(gè)長度為m的子串。我們將他們的哈希值分別取出來,然后再進(jìn)行比對(duì)。如果相等,則忽略,否則ans++
我聽說這道題用自然溢出哈希是翻車了的。故我貼這個(gè)題的目的,是想貼一下雙哈希的大概樣子。
#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;//性質(zhì)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;//預(yù)處理了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;//性質(zhì)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);//枚舉每個(gè)子串并取其哈希值 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; }轉(zhuǎn)載于:https://www.cnblogs.com/Uninstalllingyi/p/11191045.html
總結(jié)
以上是生活随笔為你收集整理的基本数据结构—Hash哈希的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: golang 学习 (八)协程
- 下一篇: 不用中间变量交换a和b的值?