【问题标题】:How to incorporate mod in rolling hash of Rabin Karp algorithm?如何在 Rabin Karp 算法的滚动哈希中加入 mod?
【发布时间】:2018-11-22 00:25:52
【问题描述】:

我正在尝试使用 mod 实现 Rabin Karp 算法。我使用的哈希函数是:

H1= c1*a^k-1 + c2*a^k-2 +c3*a^k-3 +…+ck*a^0

这里的 cx 是字符的 ASCII 值。为了滚动它,我首先通过减去第一项来删除它,然后乘以 a 并通过乘以 a^0 来添加新项。

现在的问题是处理我使用过 mod 操作的大值,但这样做我无法正确滚动它。我的代码如下:

public class RabinKarp {
private static final int base = 26;
private static final int mod = 1180637;

public static void main(String[] args) {
    String text = "ATCAAGTTACCAATA";
    String pattern = "ATA";
    char[] textArr = text.toCharArray();
    char[] patternArr = pattern.toCharArray();
    System.out.println(getMatchingIndex(textArr, patternArr));
}

public static int getMatchingIndex(char[] textArr, char[] patternArr) {
    int n = textArr.length;
    int m = patternArr.length;
    int patternHash = getHashForPatternSize(patternArr, m);
    int textHash = getHashForPatternSize(textArr, m);
    for(int i = 0; i < n-m; i++) {
        if(patternHash == textHash && checkMatch(textArr, patternArr, i, m))
            return i;
        textHash = rollingHash(textArr, textHash, i, m);    
    }
    return -1;
}

public static boolean checkMatch(char[] textArr, char[] patternArr, int i, int m) {
    for(int j = 0; j < m; j++,i++) {
        if(textArr[i] != patternArr[j])
            return false;
    }
    return true;
}

public static int rollingHash(char[] textArr, int textHash, int i, int m) {
    return (textHash * base - modularExponentiation(base, m, mod) * (int)textArr[i] + (int) textArr[i+m])%mod;
}

public static int getHashForPatternSize(char[] arr, int m) {
    int hash = 0;
    for(int i = 0, p = m; i < m; i++, p--) {
        hash = (hash%mod + calcHash(arr[i], p)%mod)%mod;
    }
    return hash;
}

public static int calcHash(char alphabet, int p) {
    return (((int) alphabet)%mod * modularExponentiation(base, p, mod)%mod)%mod;
}

public static int modularExponentiation(int base, int p, int mod) {
    if(p == 0)
        return 1;
    if(p%2 == 0)
        return modularExponentiation((base*base)%mod, p/2, mod);
    else
        return (base*modularExponentiation((base*base)%mod, (p-1)/2, mod))%mod;
}
}

问题是textHashpatternHash 在任何时候都不匹配。我确信问题出在 mod 操作上。任何人都可以告诉如何拥有 mod 以及正确使用滚动哈希。我将不胜感激。

【问题讨论】:

  • 您可能需要调试方法modularExponentation,但我认为您不需要递归地进行。您可以反复进行以降低复杂性。
  • 该方法是正确的,但问题是求幂有一定的效果,当我试图滚动它时 (textHash - calcHash(textArr[i], 0))/base - 这个除以基数不工作我想.....我的目标是让这个算法有利于更大的价值。我当然可以选择基数为 10 并选择不使用 mod 但这不符合我的目的
  • 我不知道您使用的是什么语言,但在大多数类似 C 的语言中,% 运算符无法正确计算 mod,除非两个操作数都是正数。
  • 没问题,现在都排好序了@rici

标签: string algorithm string-matching rabin-karp


【解决方案1】:

计算 Rabin-Karp 滚动哈希的常用方法是按大端顺序考虑字符,而不是小端解决方案。这使得算术更容易,因为它避免了除法。模块化除法很重要,您不能简单地将其实现为(p/q)%b

如果我们将滚动哈希作为

H<sub>0…k-1</sub> = (c<sub>0</sub>*a<sup>k-1</sup> + c<sub>1</sub>*a<sup>k-2</sup> + c<sub>2</sub>*a<sup>k-3</sup> …+… c<sub>k-1</sub>*a<sup>0</sup>) mod b

那么下一个词是:

H<sub>1…k</sub>   = (         c<sub>1</sub>*a<sup>k-1</sup> + c<sub>2</sub>*a<sup>k-2</sup> …+… c<sub>k-1</sub>*a<sup>1</sup> + c<sub>k</sub>*a<sup>0</sup>) mod b

我们可以很容易地看到这一点

H<sub>1…k</sub>   = (a * H<sub>0…k-1</sub> - c<sub>0</sub>*a<sup>k</sup> + c<sub>k</sub>) mod b

如果我们随后预计算 m == a<sup>k</sup> mod b,则变为:

H<sub>1…k</sub>   = (a * H<sub>0…k-1</sub> - m * c<sub>0</sub> + c<sub>k</sub>) mod b

每次迭代的工作量要少得多,而且完全不依赖于除法。

【讨论】:

  • 起初我实现了大端的解决方案。失败了,然后我选择了小端的。我会尽量小心地做你的
  • 我确实尝试过你的方程式,但它又失败了。我将更新问题中的代码,但老实说,您的方程式只是表示同一事物的不同方式。
  • @JotWaraich:不,他们不是。除法不适用于模运算。
  • @user2628641:因为模运算符是幂等的并且分布在加法和乘法上。第一个表示(x mod b) mod bx mod b 完全相同,第二个表示当您进行加法和乘法运算时,如果需要,您可以应用额外的mod,您通常会这样做以避免整数溢出。
  • @Backrub32:是的
猜你喜欢
  • 1970-01-01
  • 2013-12-23
  • 2023-03-09
  • 2017-07-27
  • 2012-01-18
  • 1970-01-01
  • 2016-09-06
  • 2016-08-17
  • 2021-05-26
相关资源
最近更新 更多