將每一項存在數組中,經過下標來索引。這種實現的方式問題在於:算法
解決方案:將key從string映射成int數組
解決方案:將全部可能的key映射到一個大小爲m的table中,理想狀況 m=n,n表示table中key的個數。問題:有可能形成衝突,即兩個不一樣的key計算hash以後,卻獲得了同一個keybash
使用hash函數。數據結構
h(k)=k mod mapp
這種方式選擇的m一般是與2的冪次方不太接近的質數ide
其中a是個隨機數,k包含w個bit,m通常選擇 函數
取值規則以下: ui
h(k)=[(ak+b)mod p]mod m
其中a,b是{0,..,p-1}中的隨機值,P是一個大的質數spa
若是key是同樣的,就在table的當前索引值以後加一個鏈表,指向新的加入的值,此時,最壞的狀況就是,全部的key都hash衝突,致使最壞的查找時間爲O(n).net
簡單一致hash
假設每一個key被映射到table中的任意一個索引的機率是同樣的,與其它的key經過hashing計算出來的位置無關。
在這種假設下 ,假設一共有n個key,表的大小爲m,那麼每一個鏈條的長度![]()
那麼通常狀況下,運行時間爲 O(1+α),於是能夠看到在假設的前提之下,使用鏈表解決hash衝突是個不錯的選擇
具體策略爲,hash函數包括要計算hash的key和嘗試的次數來獲得具體的下標
假設通過3次插入數據,h(586,1)=1,h(133,1)=2,h(112,1)=4
再次插入一個數據h(226,1)=4,此時產生了衝突,增長重試的次數,獲得h(226,2)=3此時尚未存儲值,能夠插入 ![]()
標記的方式用於解決,示例中的,加入刪除了112,在查找226的過程當中,計算h(226,1)==4,而以前的位置被112佔據,若是刪除112的時候置爲空,那麼此時會標記爲找不到,很明顯不正確,若是僅標記爲已經刪除則能夠解決這個問題,對於帶有刪除標記的位置,一樣能夠插入,這樣就解決了問題
這種狀況下,須要嘗試的次數爲
指望查找的時間是常量,那麼但願,考慮到m過小,查詢慢;m太大,浪費
給定兩個字符串s和t,須要判斷s是否在t中出現。
最簡單的方法是兩次遍歷:
for i in range(len(t)-len(s)):
for j in range(len(s)):
依次對比是否可以成功匹配
複製代碼
它的執行規則爲遍歷整個的字符串,而後依次去匹配短字符串s是否存在原來的數組中,沒有找到,依次後移
使用Karp-Rabin算法提升速度,對於要匹配的字符串s,能夠直接算出它的hash值,對於字符串t,須要首選獲取一個長度爲|s|的字符串,一樣能夠計算它的hash值
若是在上面的計算過程都可以在常量時間內完成,那麼總共的花銷爲O(|t|)。具體實施以下:
def rhCombinationMatch(self):
winLength = len(self.findStr)
//構建要查找的字符串RollingHash對象
winRh = RollingHashCombination(self.findStr)
lineLen = len(self.lines)
//構建要屢次計算的字符串的RollingHash對象
matchRh = RollingHashCombination(self.lines[0:winLength])
for i in range(0,lineLen-winLength+1):
//判斷兩個的hash值是否一致
if matchRh.hash() == winRh.hash():
sequence=self.lines[i:i+winLength]
# 若是一致,排除hash衝突的影響,看下字符串是否相等
if sequence == self.findStr:
self.count+=1
if i+winLength<lineLen:
//沒有匹配到,變換新的字符串,去掉第一個,加上下一個
matchRh.slide(self.lines[i],self.lines[i+winLength])
複製代碼
構建的RollingHash對象以下,它主要負責去將每個步驟組裝起來
class RollingHashCombination(object):
""" 將rolling hash的每一步組合起來 """
def __init__(self,s):
base = 7
p=499999
self.rhStepByStep = RollingHashStepByStep(base,p)
for c in s:
self.rhStepByStep.append(c)
self.chash = self.rhStepByStep.hash()
def hash(self):
return self.chash
//依次刪掉以前的值 , 添加新的值
def slide(self,preChar,nextChar):
""" 刪掉以前的值 , 添加新的值 """
self.rhStepByStep.skip(preChar)
self.rhStepByStep.append(nextChar)
self.chash = self.rhStepByStep.hash()
複製代碼
舉例假設有5個字符串爲"ABCDEF",要找的字符串長度爲3,而hash值僅根據ASCII來直接拼接,真整個計算過程匹配以下:
於是原始的字符從656667演變成了666768。假設
那麼n(1) = (n(0)-old*base^(K-1))*base+new,假設舊數字的hash值是 h1,新數字的hash是
h2=[(n(0)-old*base^(K-1))*base+new] mod p
=[(n(0) mode p)*base -old *(base^(k) mod p) +new ] mod p //對同行一個數求兩次餘數不會改變結果
複製代碼
使magic = base(k) mod p 而 h1 = n(0) mod p,h2= [h1base -oldmagic +new ]mod p
代碼實現以下,負責每一個步驟hash值的計算
class RollingHashStepByStep(object):
""" 對RollingHash進行一步一步的拆分,能夠分紅兩個步驟,每一個步驟都會生成對應的hash值 """
def __init__(self, base,p):
""" 獲得一個rollinghash初始值 """
super(RollingHashStepByStep, self).__init__()
self.base = base
# 質數
self.p = p
# 剛開始沒有元素
self.chash= 0
# 剛開始沒有元素 magic = magic ** k %p k=0
self.magic= 1
self.ibase = base ** (-1)
# 保證數據小
def append(self,newChar):
""" 在原有的hash基礎上增長一個字符,計算其hash值 """
# old 返回一個字串的 ASCII值
new10=ord(newChar)
self.chash = (self.chash * self.base + new10 ) % self.p
#滑動窗口中增長一個元素,根據magic的定義 magic是base的長度的次方
self.magic = (self.magic * self.base)
def skip(self,oldChar):
""" 在原有的hash基礎上去掉一個字符,計算其hash值 """
# hash-old*magic 多是負值 old < base magic <p
self.magic =int(self.magic * self.ibase)
# todo 進制計算,爲何傳進來的數字不須要轉換成對應的進制 在不用base的地方進行解答
old10 =ord(oldChar);
self.chash = (self.chash-old10*self.magic + self.p * self.base ) % self.p
def hash(self):
return self.chash
複製代碼