算法学习之Rabin-Karp字符串匹配(java版)

算法学习之Rabin-Karp字符串匹配(java版)

字符串匹配通常使用KMP算法,但该算法比较难记,而且应用范围不广。这里我们介绍一下Rabin-Karp算法,该算法的算法复杂度以KMP一样都是O(n+m),思路简单。

概念

给出一道字符串匹配的题目如下:

在source字符串中找到target字符串,如果能找到就输出其下标,否则输出-1。
输入:source:“abcdef” target:“cde”
输出:2

  • 思路1 暴力求解法
    直接用暴力法求解,"abc"与"cde"比较,不一致,然后右移一位"bcd"与"cde"比较…其时间复杂度为 O ( n ∗ m ) O(n*m) O(nm),其中n是source字符串长度,m是target字符串长度
  • 思路2 Rabin-Karp法
    优化暴力求解法,让每一次字符串比较的复杂度由 O ( m ) O(m) O(m)缩小到 O ( 1 ) O(1) O(1),那么整个算法的时间复杂度就是 O ( n ) O(n) O(n)。为此,我们可以将字符串的比较转化为整数的比较,那么就可以在 O ( 1 ) O(1) O(1)完成。
    在Rabin-Karp中通过哈希函数的方法,将字符串映射成为整数,不断比较source字符串中截取出来的字符串计算获得的哈希值与target字符串计算得的哈希值做比较即可。
    但是,对于哈希值来说,可能会发生冲突,key不同而value相同的情况相信大家都遇到过。因此,我们需要在value相同的情况下再对比两个字符串是否相等。那么最终该算法的复杂度为 O ( n + m ) O(n+m) O(n+m)。其中这个多出来的 O ( m ) O(m) O(m)就是最后确认字符串相等而产生的。

框架

比如在"abcdef"中找"cde"。

  1. 计算"abc"的hash函数,为 x = a ∗ 3 1 2 + b ∗ 3 1 1 + c ∗ 3 1 0 x=a*31^2+b*31^1+c*31^0 x=a312+b311+c310
  2. ( x ∗ 31 + d − a ∗ 3 1 3 ) (x*31+d-a*31^3)%10^6 (x31+da313)。这样就计算出了"bcd"的hash函数,等于整个窗口右移了一位
  3. 比较hash值和目标子串的hash值
  4. 为了避免相同hash值,而key不同的情况,在找到相同hash时,需要在比较一次该字符串。
public int strStr(String source, String target){
     
    if(source == null || target == null) return -1;
    int len = target.length();
    if(len == 0) return 0;
    int BASE = 10^6;
    int power = 1;
    for(int i = 0; i < len; i++){
     
        power = (power * 31) % BASE;
    }
    int targetCode = 0;
    int hashCode = 0;
    for(int i = 0; i < len; i++){
     
        targetCode = (targetCode * 31 + target.charAt(i)) % BASE;
    }
    for(int i =0;i<source.length();i++){
     
        hashCode = (hashCode * 31 + source.charAt(i)) % BASE;
        if(i <= len - 1) continue;
        hashCode = hashCode - (source.charAt(i - len) * power) % BASE;
        if(hashCode < 0) hashCode += BASE;
        if(hashCode == targetCode){
     
            if(source.substring(i-len+1, i+1).equals(target)){
     
                return i-len+1;
            }
        }
    }
    return -1;
}

总结

该算法相比于KMP算法更容易理解与记忆,需要注意的是注意溢出的情况,所以几处取模不可省略。该算法的时间复杂度是 O ( n + m ) O(n+m) O(n+m)

你可能感兴趣的:(算法学习,算法,字符串,java)