【问题标题】:Why are two different hashes being generated for the same subStrings and what can I do to solve this?为什么会为相同的子字符串生成两个不同的哈希值,我能做些什么来解决这个问题?
【发布时间】:2020-05-17 06:45:10
【问题描述】:

我编写了以下代码来尝试 Rabin-Karp 算法的简单实现。

 public int charToInt(int index, String str){
        return (int)str.charAt(index);
    }

    public int strStr(String haystack, String needle) {
        if(needle.length() == 0 ) return 0;
        int n = needle.length();
        int l = haystack.length();
        if(n > l) return -1;

        //choose large enough prime for hash
        final int prime = 257;

        //calculate reference hash of needle and first 'n' chars of haystack
        long refHash = 0, rollHash = 0;
        for(int i = 0; i < n; i++){
            refHash += charToInt(i,needle)*(long)Math.pow(prime,i);
            rollHash += charToInt(i,haystack)*(long)Math.pow(prime,i);
        }
        System.out.println("refHash: "+refHash);
        System.out.println("rolling hash: "+rollHash);
        if(refHash == rollHash) return 0;

        for(int i = n; i<l; i++){
            // oldhash - old initial char
            rollHash -= charToInt(i-n+1, haystack);
            // divide by prime.
            System.out.println("Perfect division anticipated "+ (double)rollHash/prime);
            rollHash /= prime;
            // add new char to hash at the end of pattern.
            rollHash += (charToInt(i,haystack)*(long)Math.pow(prime,n-1));

            if(refHash == rollHash) return i-n+2;
            System.out.println("rolling hash: "+rollHash);
        }
        return -1;
    }

像上面的代码一样计算滚动散列在纸上效果很好,但我无法弄清楚为什么 rollHash /= prime; 没有产生完美的除法。

一个示例输入/输出,希望能提供更多上下文。 输入

haystack: "hello"
needle: "ll"

输出

stdout:
refHash: 27864
rolling hash: 26061
Perfect division anticipated 101.01167315175097
rolling hash: 27857
Perfect division anticipated 107.9727626459144
rolling hash: 27863
Perfect division anticipated 107.99610894941634
rolling hash: 28634
Answer:
-1

我希望 Perfect division anticipated 107.9727626459144 这一行将输出 108,而 rolling hash: 27863 滚动哈希将是 27864。

【问题讨论】:

    标签: java math hash rabin-karp


    【解决方案1】:

    让我们想想rollHash 的结构。让A[]needle 的任何字符值数组。在第一个循环之后rollHash 将是

    A[0] + prime * A[1] + prime^2 * A[2] + ...
    

    在第二个循环中

    for(int i = n; i<l; i++){
            // oldhash - old initial char
            rollHash -= charToInt(i-n+1, haystack);
            // divide by prime.
            System.out.println("Perfect division anticipated "+ (double)rollHash/prime);
            rollHash /= prime;
            ....
    }
    

    在第一次迭代中,i = n,我们减去 A[i-n+1] = A[1]。所以rollhash 现在是

    A[0] + prime * A[1] + prime^2 * A[2] + ... - A[1]
    

    我们不希望它可以被素数整除。

    我认为你有一个错误。

    for(int i = n; i<l; i++){
            // oldhash - old initial char
            rollHash -= charToInt(i-n, haystack);    // **** changed
            // divide by prime.
            System.out.println("Perfect division anticipated "+ (double)rollHash/prime);
            rollHash /= prime;
            ....
    }
    

    这现在给出了完美的划分,算法似乎在有限的测试数据上给出了正确的结果。

    另请注意Math.pow(i,j) 是一个相对昂贵的功能,并且很容易消除它的使用。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-11-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多