轉載:字符串hash總結(hash是一門優雅的暴力!)

轉載自:遠航休息棧php

字符串Hash總結

Hash是什麼意思呢?某度翻譯告訴咱們:算法

hash 英[hæʃ] 美[hæʃ]
n. 剁碎的食物; #號; 蔬菜肉丁;
vt. 把…弄亂; 切碎; 反覆推敲; 搞糟;sublime-text

我以爲Hash是引伸出 把...弄亂 的意思。api

今天就來談談Hash的一種——字符串hash。

據個人理解,Hash就是一個像函數同樣的東西,你放進去一個值,它給你輸出來一個值。輸出的值就是Hash值。通常Hash值會比原來的值更好儲存(更小)或比較。
數組

那字符串Hash就很是好理解了。就是把字符串轉換成一個整數的函數。並且要儘可能作到使字符串對應惟一的Hash值。函數

字符串Hash的種類仍是有不少種的,不過在信息學競賽中只會用到一種名爲「BKDR Hash」的字符串Hash算法。優化

它的主要思路是選取恰當的進制,能夠把字符串中的字符當作一個大數字中的每一位數字,不過比較字符串和比較大數字的複雜度並無什麼區別(高精數的比較也是O(n)O(n)的),但只要把它對一個數取模,而後認爲取模後的結果相等原數就相等,那麼就能夠在必定的錯誤率的基礎上O(1)O(1)進行判斷了。ui

那麼咱們選擇什麼進制比較好?atom

首先不要把任意字符對應到數字0,好比假如把a對應到數字0,那麼將不能只從Hash結果上區分ab和b(雖然能夠額外判斷字符串長度,但不把任意字符對應到數字0更加省事且沒有任何反作用),通常而言,把a-z對應到數字1-26比較合適。spa

關於進制的選擇實際上很是自由,大於全部字符對應的數字的最大值,不要含有模數的質因子(那還模什麼),好比一個字符集是a到z的題目,選擇2七、23三、19260817都是能夠的。

模數的選擇(儘可能仍是要選擇質數):

絕大多數狀況下,不要選擇一個109109級別的數,由於這樣隨機數據都會有Hash衝突,根據生日悖論,隨便找上109−−−√109個串就有大機率出現至少一對Hash 值相等的串(參見BZOJ 3098 Hash Killer II)。

最穩妥的辦法是選擇兩個109109級別的質數,只有模這兩個數都相等才判斷相等,但常數略大,代碼相對難寫,目前暫時沒有辦法卡掉這種寫法(除了卡時間讓它超時)(參見BZOJ 3099 Hash Killer III)。

若是能背過或在考場上找出一個10181018級別的質數(Miller-Rabin),也相對靠譜,主要用於前一種擔憂會超時,後一種擔憂被卡。

偷懶的寫法就是直接使用unsigned long long,不手動進行取模,它溢出時會自動對264264進行取模,若是出題人比較良心,這種作法也不會被卡,但這個是徹底能夠卡的,卡的方法參見BZOJ 3097 Hash Killer I。

用luogu P3370爲例。

這是天然溢出hash(100)

 

這是單模數hash(80)

 

這是雙hash(100)

 

這是隻用一個10^18質數的hash(100)

 

Hash還有一方面,就是它能夠處理子串信息。對於一個字符串,咱們能夠預處理它1l1−l的hash值,這樣l+1l+1的hash值就能夠O(1)O(1)的遞推出來。

對於一個字符串lrl−r的子串,咱們能夠用f[r]brl+1f[l1]f[r]−br−l+1f[l−1]來求出來,其中b表示進制。

這樣的話hash就能夠水過字符串匹配的題目

cogs1570

【題目描述】

法國做家喬治·佩雷克(Georges Perec,1936-1982)曾經寫過一本書,《敏感字母》(La disparition),全篇沒有一個字母‘e’。他是烏力波小組(Oulipo Group)的一員。下面是他書中的一段話:

Tout avait Pair normal, mais tout s’affirmait faux. Tout avait Fair normal, d’abord, puis surgissait l’inhumain, l’affolant. Il aurait voulu savoir où s’articulait l’association qui l’unissait au roman : stir son tapis, assaillant à tout instant son imagination, l’intuition d’un tabou, la vision d’un mal obscur, d’un quoi vacant, d’un non-dit : la vision, l’avision d’un oubli commandant tout, où s’abolissait la raison : tout avait l’air normal mais…

佩雷克極可能在下面的比賽中獲得高分(固然,也有多是低分)。在這個比賽中,人們被要求針對一個主題寫出甚至是意味深長的文章,而且讓一個給定的「單詞」出現次數儘可能少。咱們的任務是給評委會編寫一個程序來數單詞出現了幾回,用以得出參賽者最終的排名。參賽者常常會寫一長串廢話,例如500000個連續的‘T’。而且他們不用空格。

所以咱們想要儘快找到一個單詞出現的頻數,即一個給定的字符串在文章中出現了幾回。更加正式地,給出字母表{'A','B','C',...,'Z'}和兩個僅有字母表中字母組成的有限字符串:單詞W和文章T,找到W在T中出現的次數。這裏「出現」意味着W中全部的連續字符都必須對應T中的連續字符。T中出現的兩個W可能會部分重疊。

【輸入格式】

輸入包含多組數據。

輸入文件的第一行有一個整數,表明數據組數。接下來是這些數據,以以下格式給出:

第一行是單詞W,一個由{'A','B','C',...,'Z'}中字母組成的字符串,保證1<=|W|<=10000(|W|表明字符串W的長度)

第二行是文章T,一個由{'A','B','C',...,'Z'}中字母組成的字符串,保證|W|<=|T|<=1000000。

【輸出格式】

對每組數據輸出一行一個整數,即W在T中出現的次數。

【樣例輸入】

3
BAPC
BAPC
AZA
AZAZAZA
VERDI
AVERDXIVYERDIAN

【樣例輸出】

1
3
0

代碼

 

寫到這裏忽然發現hash好像能夠暴力水過不少字符串算法。。

一、kmp

問題:給兩個字符串S1,S2,求S2是不是S1的子串,並求S2在S1中出現的次數

把S2 Hash出來,在S1裏找全部長度爲|S2||S2|的子串,Hash比較。效率O(|S1|)O(|S1|)

二、AC自動機

問題:給N個單詞串,和一個文章串,求每一個單詞串是不是文章串的子串,並求每一個單詞在文章中出現的次數。

把每個單詞hash成整數,再把文章的每個子串hash成整數,接下來只須要進行整數上的查找便可。

複雜度:O(|A|2+|S|)O(|A|2+|S|)

用AC自動機能夠作到O(|A|+|S|)O(|A|+|S|)的複雜度,|S||S|是單詞串總長,|A||A|是文章長度

三、後綴數組

問題:給兩個字符串S1,S2,求它們的最長公共子串的長度。

將S1的每個子串都hash成一個整數,將S2的每個子串都hash成一個整數

兩堆整數,相同的配對,而且找到所表示的字符串長度最大的便可。

複雜度:O(|S1|2+|S2|2)O(|S1|2+|S2|2)

用後綴數組能夠優化到O(|S|log|S|)O(|S|log|S|)

四、馬拉車

問題:給一個字符串S,求S的最長迴文子串。

先求子串長度位奇數的,再求偶數的。枚舉迴文子串的中心位置,而後二分子串的長度,直到找到一個該位置的最長迴文子串,不斷維護長度最大值便可。

複雜度:O(|S|log|S|)O(|S|log|S|)

用manacher能夠作到O(|S|)O(|S|)的複雜度

五、擴展kmp

問題:給一個字符串S,求S的每一個後綴與S的最長公共前綴

枚舉每個後綴的起始位置,二分長度,求出每一個後綴與S的最長公共前綴。

複雜度:O(|S|log|S|)O(|S|log|S|)

用extend-kmp能夠作到O(|S|)O(|S|)的複雜度

後記

hash真是一種優雅的暴力。

由於字符串特殊的性質,咱們能夠二分得處理它,通常都有單調性。

相關文章
相關標籤/搜索