是否有任何在Rabin-Karp字符串搜索算法中使用的滚动哈希函数的工作实现?

2022-09-04 23:34:52

我希望使用滚动哈希函数,这样我就可以获取一个非常大的字符串的n-gram的哈希值。

例如:

“stackoverflow”,分解成5克将是:

“stack”, “tacko”, “ackov”, “ckove”, “kover”, “overf”, “verfl”, “erflo”, “rflow”

这对于滚动哈希函数来说是理想的,因为在我计算第一个n-gram哈希之后,下面的哈希相对便宜,因为我只需要删除第一个哈希的第一个字母并添加第二个哈希的新的最后一个字母。

我知道通常这个哈希函数生成如下:

H = c1ak − 1 + c2ak − 2 + c3ak − 3 + ... + cka0,其中 a 是常量,c1,...,ck 是输入字符。

如果你在Rabin-Karp字符串搜索算法上点击这个链接,它指出“a”通常是一些大素数。

我希望我的哈希值存储在32位整数中,那么“a”应该有多大的素数,这样我就不会溢出我的整数?

在某个地方是否存在此哈希函数的现有实现,我已经可以使用?


这是我创建的一个实现:

public class hash2
{

    public int prime = 101;

    public int hash(String text)
    {
        int hash = 0;

        for(int i = 0; i < text.length(); i++)
        {
            char c = text.charAt(i);
            hash += c * (int) (Math.pow(prime, text.length() - 1 - i));
        }

        return hash;
    }

    public int rollHash(int previousHash, String previousText, String currentText)
    {

        char firstChar = previousText.charAt(0);
        char lastChar = currentText.charAt(currentText.length() - 1);

        int firstCharHash = firstChar * (int) (Math.pow(prime, previousText.length() - 1));
        int hash = (previousHash - firstCharHash) * prime + lastChar;

        return hash;
    }

    public static void main(String[] args)
    {
        hash2 hashify = new hash2();

        int firstHash = hashify.hash("mydog");
        System.out.println(firstHash);
        System.out.println(hashify.hash("ydogr"));
        System.out.println(hashify.rollHash(firstHash, "mydog", "ydogr"));
    }

}

我用101作为我的巅峰。我的哈希值是否会溢出有关系?我认为这是可取的,但我不确定。

这似乎是正确的方法吗?


答案 1

我记得一个稍微不同的实现,它似乎来自sedgewick的算法书之一(它还包含示例代码 - 尝试查找它)。以下是调整为 32 位整数的摘要:

使用模算术来防止整数在每次运算后溢出。

初始设置:

  • c = 文本(“堆栈溢出”)
  • M = “n 元语法”的长度
  • d = 字母表的大小 (256)
  • q = 一个大素数,以便 (d+1)*q 不会溢出(8355967可能是一个不错的选择)
  • dM = dM-1 mod q

首先计算第一个 n 元语法的哈希值:

h = 0
for i from 1 to M:
  h = (h*d + c[i]) mod q

并且对于以下每个 n 元语法:

for i from 1 to lenght(c)-M:
  // first subtract the oldest character
  h = (h + d*q - c[i]*dM) mod q

  // then add the next character
  h = (h*d + c[i+M]) mod q

在减去最旧的字符之前必须添加 d*q 的原因是,由于上一个模运算导致的小值,您可能会遇到负值。

包括错误,但我认为你应该明白这个想法。尝试找到一本sedgewick的算法书籍,以获取详细信息,更少的错误和更好的描述。:)


答案 2

据我所知,这是一个函数最小化:

2^31 - sum (maxchar) * A^kx

其中(用于 )。我刚刚通过Excel(确切地说是OO Calc)计算了它:)它找到的 max A 是 质数的 , 或 。maxchar = 62A-Za-z0-97673