【问题标题】:Rabin Karp Algorithm Negative HashRabin Karp 算法负散列
【发布时间】:2022-01-18 01:05:26
【问题描述】:

我有这个 Rabin Karp 实现。现在我对滚动哈希所做的唯一事情就是从sourceHash 中减去power*source[i]power31^target.size()-1 % mod 但我不明白为什么当它变成负数时我们将mod 添加到sourceHash。我尝试添加其他值,但它不起作用,并且仅在我们添加 mod 时才起作用。为什么是这样?我们添加 mod 而不是其他任何内容(例如随机大数字)是否有特定原因。

int rbk(string source, string target){
        int m = target.size();
        int n = source.size();
        int mod = 128;
        int prime = 11;
        int power = 1;
        int targetHash = 0, sourceHash = 0;
        for(int i = 0; i < m - 1; i++){
            power =(power*prime) % mod;
        }
        for(int i = 0; i < target.size(); i++){
            sourceHash = (sourceHash*prime + source[i]) % mod;
            targetHash = (targetHash*prime + target[i]) % mod;
        }
        
        for(int i = 0; i < n-m+1; i++){
            if(targetHash == sourceHash){
                bool flag = true;
                for(int j = 0; j < m; j++){
                    if(source[i+j] != target[j]){
                        flag = false;
                        break;
                    }
                }
                if(flag){
                    return 1;
                }
            }
            
            if(i < n-m){
                sourceHash = (prime*(sourceHash - source[i]*power) + source[i+m]) % mod;
                if(sourceHash < 0){
                    sourceHash += mod;
                }
            }
        }
        return -1;
}

【问题讨论】:

  • 确保它在区间 [0, mod) 内。
  • 是不是因为你应该使用无符号类型但使用的是有符号类型?
  • @MadPhysicist 我试过只是翻转标志,但结果是错误的答案。
  • 没有。基本思想是将无限空间映射到有限空间。 mod 做得很好,因为无论输入数字如何,它都会循环回到区间。另一方面,如果你使用一个大常数,总有一些数字加到这个大数字上会落在可用有限空间之外。
  • @user1984 这仍然让我感到困惑,因为即使我们以某种方式使 sourceHash 为正,即使在 0 到 mod 的范围内,其他值仍然不能给我们一个好的答案.我尝试过尽可能接近 mod 值的数字,但我只是不明白为什么只有 mod 有效。在某些情况下修改sourceHash不应该以某种方式产生不一致吗?

标签: c++ algorithm rabin-karp


【解决方案1】:

当使用模运算(mod n) 时,我们只有n 不同 数字:0, 1, 2, ..., n - 10 .. n - 1out 的所有其他数字都等于0 .. n - 1 中的某个数字:

-n     ~ 0
-n + 1 ~ 1
-n + 2 ~ 2
 ...
-2     ~ n - 2
-1     ~ n - 1
   

 n     ~ 0
 n + 1 ~ 1
 n + 2 ~ 2
 ...
 2 * n     ~ 0
 2 * n + 1 ~ 0

一般情况下A ~ B 当且仅当(A - B) % n = 0(这里% 代表余数)。

在实现 Rabin Karp 算法时,我们可能会遇到两个潜在问题:

  1. 哈希可能太大,我们会面临整数溢出
  2. 负余数可以在不同的编译器上以不同的方式实现:-5 % 3 == -2 == 1

为了处理这两个问题,我们可以规范化余数,并仅对 safe 0 .. n - 1 范围内的数字进行操作。 对于任意值A,我们可以放

 A = (A % n + n) % n;

【讨论】:

  • @Mayank: if(sourceHash &lt; 0) sourceHash += mod; 只能部分解决问题:如果sourceHash 太大,您可能会面临整数溢出。输入sourceHash = (sourceHash % mod + mod) % mod;,保证sourceHash0 .. mod - 1范围内
  • 好的,如果我理解正确的话。具有负值的模数可以在不同的编译器上给出不同的答案。所以如果说-10%3,答案可能是-12。在 python3 中,-10%3 给出2。在 C++14 中,它给了我-1。所以我们显然不想要否定的答案,因为那是不正确的。为了使否定答案看起来好像计算正确,我们可以简单地添加 mod 以获得正值。就好像它没有计算负模,而是直接给出了 Python3 之类的答案。我希望我的解释是正确的。
  • @Mayank:你说得很对,唯一的细节是-10 % 3 == -1 也是正确的,但不是规范化,从技术上讲,这会导致错误的答案(数学公式使用标准0 .. mod - 1 范围)。
猜你喜欢
  • 2021-05-26
  • 2022-01-10
  • 1970-01-01
  • 2018-04-09
  • 2012-04-09
  • 2022-08-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多