Karp Rabin 算法是利用hash函數的特性進行字符串匹配的。算法
KR算法對模式串和循環中每一次要匹配的子串按必定的hash函數求值,若是hash值相同,才進一步比較這兩個串是否真正相等app
本文使用的hash函數以下:函數
hash(str[0..m-1])=(str[0]*2^(m-1)+str[1]*2^(m-2)+……+str[m-1]*2^0)mod q性能
q是一個較大的數,並且最好是素數,並且是大於m的素數。spa
計算時,*2的運算就使用移位代替了.net
在下面的實現代碼中,q取整形最大值,也就是說,能夠不用進行模運算了,這種偷工減料的作法只能在模式串很短的情形下才能夠用,要否則會溢出的。粗略算了一下,若是模式串是ascii 英文字符,那麼模式串長度不超過25的狀況下,在32 位機上是沒問題的orm
舉例說一下KR算法吧:blog
在我寫的這個總結中,各個算法使用的例子都同樣,隨便找的;ci
模式串 pattern="pappar"字符串
文本串 text="pappappapparrassanuaragh";
pattern 長度記 pattern_len
預備階段就是計算pattern的hash,長度爲6,那麼hash_pattern='p'*2^5+'a'*2^4+'p'*2^3+'p'*2^2+'a'*2^1+'r'*2^0
固然,這裏使用的是字符的ascii值
也計算text前六個字符的hash,咱們記第一個爲hash_text[0]
而後就開始向前移動了,在移動時,要從新計算當前與模式串對應的串的hash值,這個工做叫rehash
初始化 i=0
若是 hash_pattern與hash_text[i]相等,返回 i
若是不等 計算新的hash值,就是text[i..i+patten_len]的hash,
固然這裏不會像第一次那樣所有計算,方法是
上一次計算的值減去上一次匹配時串的第一個字符乘以 2^pattern_len ,而後乘以2,再加上新加入比較的字符值
根據公式能夠很清晰看出來。
就是減去計算中的第一項,把剩下的乘以2,而後在末尾加入新增的字符值
看代碼吧,很簡答的
[cpp] view plaincopy
//Karp-Rabin algorithm,a simple edition
int karp_rabin_search(const char* text,const int text_len,const char* pattern,const int pattern_len)
{
int hash_text=0;
int hash_pattern=0;
int i;
//rehash constant:2^(pattern_len-1)
int hash_const=1;
/*for (i=1;i<pattern_len;i++){
hash_const<<=1;
}*/
hash_const<<=pattern_len-1;
//preprocessing
//hashing
for (i=0;i<pattern_len;++i){
hash_pattern=(hash_pattern<<1)+pattern[i];
hash_text=(hash_text<<1)+text[i];
}
//searching
for (i=0;i<=text_len-pattern_len;++i){
if (hash_pattern==hash_text&&memcmp(text+i,pattern,pattern_len)==0){
return i;
}else{
//rehash
hash_text=((hash_text-text[i]*hash_const)<<1)+text[i+pattern_len];
}
}
return -1;
}
hash函數的好壞會直接影響算法的效率,通常應遵循這樣的規則:
1 要容易計算,本文中用的就不錯,移位的速度你們是知道的
並且在rehash,就是從新計算hash值時,hash的構造要能避免從新計算整個串的hash,而應該像本例中用到的那樣,能夠動態地很容易地更新
2 字符串hash值要儘可能分佈均勻,減小衝突,這是hash函數在任何場合的要求。作到這一點,就能減小匹配中字符的一個個比較,提升性能。若是可以保證每一個串的hash值不一樣,就不用再比較字符了,能夠省掉代碼中的memcmp運算
Monte Carlo改進的 RK算法就是隻比較hash值,雖然那個改進的算法不能保證正確的結果,但以低於2.53/pattern_len的錯誤率,而很實用