淺談字符串Hash


淺談字符串Hash

本篇隨筆講解Hash(散列表)的一個重要應用:字符串Hash。

關於Hash

Hash是一種數據結構,叫做Hash表(哈希表),也叫散列表。關於Hash的實現,其實與離散化頗為類似。就是把若干的復雜的信息映射到一個比較容易維護的值域去。具體的實現方式是散列函數,即Hash函數,其原理是對於一個數據,選取一個關鍵鍵值\(k\),那么這個數據在Hash表中的位置就是\(f(k)\)。那么這個對應關系(\(f()\))就是哈希函數。

哈希沖突

因為我們只是定義了一個\(f()\)作為哈希函數,並沒有定義這個\(f()\)到底是什么樣的函數。所以顯然地,可能會出現一個函數\(f(k)\),使得兩個不同的\(k(k_1\not=k_2)\)而出現函數值相等的情況\(f(k_1)=f(k_2)\)(比如二次函數)。這種情況被稱作哈希沖突。導致答案錯誤。

關於字符串Hash

字符串哈希就是在字符串上進行哈希。字符串哈希的用處是快速判斷兩個字符串是否相等

一個簡單的思想:將一個字符串轉化為一個整數,這樣,只需要比較整數相不相等就可以判斷這個串是否重復出現過(不考慮哈希沖突)。

但是這個思想不考慮哈希沖突,不代表我們實現的時候也肯定不會出現哈希沖突。那么,我們當然希望我們哈希的東西(哈希映射,散列函數)是一個單射。

那么,我們用什么方法來避免哈希沖突呢?

自然溢出

給定一個字符串\(S=s_1s_2s_3\cdots s_n\),我們規定:\(f(x)=x-'a'+1\).

那么,自然溢出的哈希公式可以如此這么表示:

\[hash[i]=hash[i-1]*p+f(i) \]

注意,這里的哈希數組是\(unsigned\, long\, long\)范圍的,所以可以通過自然溢出而自動取模\(2^{64}-1\)

單Hash法

單Hash法的哈希公式可以這么表示:

\[hash[i]=(hash[i]*p)+f(i)\%mod\quad (p<mod) \]

顯然,\(mod\)越小越容易沖突,所以我們把\(p,mod\)都盡量取大即可。

這種方法仍然存在哈希沖突的概率,只不過特別低。實際運用的時候可以使用這種方法。除非你RP低到一定境界,否則不會被卡掉。

例如:

\(p=13,mod=101\),對\(abc\)進行如上所示的\(hash\)

\(hash[0]=1\)(從0開始計算)

\(hash[1]=15\)

\(hash[2]=97\)

那么97就是\(abc\)的hash值。

雙hash法

hash一遍可能還會出現沖突,那我hash兩遍。這就是雙Hash法。

開一個二元組,用兩個不同的\(mod\)來分別\(Hash\),得到的兩個結果存進二元組,作為哈希的結果。

同理,你還可以開一個結構體,多哈希幾遍(強迫症患者)(滑稽.jpg)

子串Hash

字符串子串是個常見的問題,變式很多。

但是,顯然的是,如果我們求出了一個大串的hash,就能以\(O(1)\)的時間求解其子串的哈希值。

來讓我們解釋一下這個“顯然”的含義:

\(hash[1]=s1\)
\(hash[2]=s1∗p+s2\)
\(hash[3]=s1∗p2+s2∗p+s3\)
\(hash[4]=s1∗p3+s2∗p2+s3∗p+s4\)
\(hash[5]=s1∗p4+s2∗p3+s3∗p2+s4∗p+s5\)
現在展示的是1-5的哈希。

如果我們要求s3s4的哈希,容易得出(根據哈希公式):\(s_3\times p+s_4\)。我們把\(hash[4],hash[2]\)進行消元處理的話,就能將結果中帶有\(s_1,s_2\)的系數消掉,而這個操作只需要乘上\(p^2\)即可。

那么:

\[hash[4]-hash[2]\times p^{4-3+1=2} \]

那么我們處理出通項公式:

\[hash[i]-hash[j-1]\times p^{i-j+1} \]

取模的時候為了防止減法運算出現負數,還要做如下處理:

\[hash=((hash[i]-hash[j-1]\times p^{j-i+1})\%mod+mod)\%mod \]

這就完事了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM