寫給萌新的字符串hash算法,語言不嚴謹就算了,固然也歡迎dalao指點QAQ算法
$hash$是一種映射,在信息學中能夠用於將一些不方便做爲下標儲存的結構看成一個數來存起來,方便$O$(1)的查找,可能不太好用,可是思惟極其重要優化
模板:求兩個字符串之間是否存在包含關係spa
KMP模板題a字符串
例如$bc$和$cbca$這兩個串,$bc$在$cbca$中出現過,它們存在包含關係,那咱們是怎麼看出來的呢hash
試着模擬一下尋找過程:$bc$串長度小一些,必定是用它來作模式串(字串)。從文本串(母串)中每次找一個長度爲2的短串,看是否是和模式串同樣的,一直找到結尾,在這裏的$cbca$就分別有三個短串($cb$、$bc$、$ca$),咱們發現了第二次的$bc$和模式串同樣,因此就判斷出來找到了。模板
若是常規來作,時間爲$O$(母串中短串個數*字串長度),約爲$O$(mn)程序
首先枚舉短串這一步無法優化了(要是有就不是$hash$了),因此主要優化找到短串後怎麼$O$(1)的和模式串判斷相不相同數據
$hash$的特徵就是不一樣的$key$(就是目標位置)對應的數據不一樣,因此將字符串轉化爲數字應該注意一一對應,避免哈希衝突(好比不一樣字符串對應了同一個值,可是你的程序仍是會判斷它們是同一個字符串)語言
通常的字符串$hash$值求法(終於到正題了)時間
給一個字符串
從左到右枚舉字符串的每一位,每個字母直接對應它的ASCII碼(就變成$int$了),對應好了就把每一位加起來,就愉快的衝突了
直接相加會衝突,例如$ab$和$ba$,第二串後來的那個$a$和第一串的前面的$a$雖然一個更老一個更年輕,可是它們的做用竟然是同樣的,這是不符合常識的(我是在說實話)
因此,爲了使資質更老的$a$更顯眼,能夠在處理以後的那些後代的時候給它乘上一個數$base$顯示它的不一樣。若是考慮到每個字符後面都有後代的話,那麼每處理一個後面的字符,前面的祖宗們就都會乘上一個數。容易看出,每個位置都比它後面那個位置多乘了一次,這樣就能夠顯示出各個位置的等級差距了2333
再結合以前的直接相加,就能夠表示出來每個不一樣的字符串了,即:
val ["abc"] = 'a'$$base^2+'b'$$base^1+'c'$*$base^0
那麼對於一個母串,怎麼提取它[ $l$ , $r$ ]中的$hash$值呢。咱們已經知道了這個串從1到每一個位置這一部分的$hash$值,這相似於前綴和,即$hash$[ $r$ ]-$hash$[ $l$-1 ],可是因爲對於$r$位置的$hash$[ $r$ ],它前面一部分(即被它包含在內的$hash$[ $l$ ]部分)被多乘了許屢次$base$,減的時候應該給$hash$[ $l$ ]也乘上
(換個說法:求出$hash$[ $l$-1 ]以後,繼續向後面走,每走一步都會$hash$[ $l$-1 ]乘上$base$,一直求到$hash$[ $r$ ]時已經乘了($r$-$l$+1)個$base$了,實際上
hash[ r ]=hash[ l,r ]+hash[ l-1 ]$*$base^(r-l+1))
因此答案應該是(多乘了的次數即[ $l$,$r$ ]區間長度)
val[ l , r ]=hash[ r ]-hash[ l-1 ]$*$base^(r-l+1)
最後,由於乘的$base$通常很大,因此乘多了容易爆,要取模,爲了不麻煩,通常使用$unsigned$ $long$ $long$
Q:hash如何支持單點修改?
A:能夠用線段樹維護
要用線段樹維護要資瓷區間合併->
hash=左子樹hash*(base^右子樹size)+右子樹hash