滚动hash实现字符串匹配

滚动哈希:常数时间生成哈希码

生成一个长度为 L 数组的哈希码,需要 O(L) 时间。
如何在常数时间生成滑动窗口数组的哈希码?利用滑动窗口的特性,每次滑动都有一个元素进,一个出
由于只会出现小写的英文字母,因此可以将字符串转化成值为 0 到 25 的整数数组: arr[i] = (int)S.charAt(i) - (int)‘a’。
按照这种规则,abcd 整数数组形式就是 [0, 1, 2, 3],转换公式如下所示。
h0
可以将上面的公式写成通式,如下所示。其中 ci为整数数组中的元素,a = 26,其为字符集的个数。类似于26进制数转换成10进制数一样。
在这里插入图片描述

下面来考虑窗口从 abcd 滑动到 bcde 的情况。这时候整数形式数组从 [0, 1, 2, 3] 变成了 [1, 2, 3, 4],
数组最左边的 0 被移除,同时最右边新添了 4。滑动后数组的哈希值可以根据滑动前数组的哈希值来计算,
计算公式如下所示。
在这里插入图片描述

写成通式如下所示。
在这里插入图片描述

如何避免溢出:

aL可能是一个很大的数字,因此需要设置数值上限来避免溢出。
设置数值上限可以用取模的方式,即用 h % modulus 来代替原本的哈希值。

hash碰撞问题:
最后如果得到的hash之相同,还要检查子串和目标串是否相同

复杂度分析时间复杂度:
  O(N),计算 needle 字符串的哈希值需要 O(L) 时间,之后需要执行 (N−L) 次循环,每次循环的计算复杂度为常数。
空间复杂度:O(1)。

Rabin-Karp算法被称道的三个原因

  1. 它可以用来检测抄袭,因为它能够处理多模式匹配;
  2. Rabin-Karp算法能够有效地检测抄袭
  3. 虽然在理论上并不比暴力匹配法更优,但在实际应用中它的复杂度仅为O(n+m);

如果能够选择一个好的哈希函数,它的效率将会很高,而且也易于实现。

Rabin-Karp算法被诟病的两个原因

  1. 有许多字符串匹配算法的复杂度小于O(n+m);
  2. 有时候它和暴力匹配法一样慢,并且它需要额外空间。

结语:
Rabin-Karp算法之所以出众最大的原因就是它可以对多模型进行匹配。
这一特性使得它在检测抄袭方面(尤其是大篇幅文字)非常好用。

 1 package 字符串匹配;
 2 
 3 public class 滚动hash {
 4 
 5     public static int strStr(String haystack, String needle) {
 6         int n = haystack.length(), L = needle.length();
 7         if(L > n) return -1;
 8         
 9         char[] src = haystack.toCharArray(), target = needle.toCharArray();
10         int a = 128, h0 = 0, L0 = 0, mod = 1 << 30;
11         
12         for (int i = 0; i < L; i++) {
13             h0 = (h0 * a + src[i]) % mod;
14             L0 = (L0 * a + target[i]) % mod;
15         }
16         if(h0 == L0 && needle.equals(haystack.substring(0, L))) {
17             return 0;
18         }
19         int al = 1;
20         for (int i = 1; i < L; i++) {
21             al = al * a % mod;
22         }
23         for (int i = L; i < n; i++) {
24             h0 = ((h0 - src[i-L] * al) * a + src[i]) % mod;
25             if(h0 == L0 && needle.equals(haystack.substring(i - L + 1, i + 1))) {
26                 return i - L + 1;
27             }
28         }
29         return -1;
30     }
31     public static void main(String[] args) {
32         System.out.println(strStr("3热3火irnr3陈849Irma,0493从da", "rma,"));
33     }
34 }

 

posted @ 2020-09-08 15:25  看完这篇还有亿篇  阅读(836)  评论(0编辑  收藏  举报