字符串匹配算法SMA 总结之二:KR ,Karp Rabin 算法

Karp Rabin 算法是利用hash函数的特性进行字符串匹配的。

KR算法对模式串和循环中每一次要匹配的子串按一定的hash函数求值,如果hash值相同,才进一步比较这两个串是否真正相等

也许你会有这样的疑问,在Brute force暴力匹配中,每一次都把模式串和文本当前字串匹配,现在每一次都计算hash,还要进一步比较,会不会更慢呢?

答案肯定是不会啦,而且事实上KR算法效率很高

第一:不同子串hash值相同是小概率事件

第二:hash函数设计合理的情况下,计算速度相当快

第三:虽然理论上KR算法的时间复杂度是O(m*n),但现实应用中一般是O(m+n)

 

本文使用的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的素数。

计算时,*2的运算就使用移位代替了

 

在下面的实现代码中,q取整形最大值,也就是说,可以不用进行模运算了,这种偷工减料的做法只能在模式串很短的情形下才可以用,要不然会溢出的。粗略算了一下,如果模式串是ascii 英文字符,那么模式串长度不超过25的情况下,在32 位机上是没问题的

 

举例说一下KR算法吧:

在我写的这个总结中,各个算法使用的例子都一样,随便找的;

模式串 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,然后在末尾加入新增的字符值

 

看代码吧,很简答的

//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的错误率,而很实用

 

 

 

 

你可能感兴趣的:(字符串匹配算法SMA 总结之二:KR ,Karp Rabin 算法)