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

标签 c# java algorithm hash rabin-karp

我希望使用滚动哈希函数,这样我就可以对非常大的字符串的 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 string search algorithm 上点击此链接,它指出“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 作为素数。我的哈希是否会溢出有关系吗?我认为这是可取的,但我不确定。

这看起来是解决此问题的正确方法吗?

最佳答案

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

您使用模运算来防止您的整数在每次操作后溢出。

初始设置:

  • c = text ("stackoverflow")
  • M = “n-grams”的长度
  • d = 字母表的大小 (256)
  • q = 一个大素数,这样 (d+1)*q 就不会溢出(8355967 可能是个不错的选择)
  • dM = dM-1 mod q

首先计算第一个n-gram的哈希值:

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

对于接下来的每一个 n-gram:

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 的算法书籍之一以获取详细信息、更少的错误和更好的描述。 :)

关于c# - Rabin-Karp 字符串搜索算法中使用的滚动哈希函数是否有任何有效的实现?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2314193/

相关文章:

c# - 在 C# 中找到两个集合的补集的最快方法

具有观察者模式的 Java MVC 不同步不同 View

algorithm - 计算给定线的交点数

c# - CaSTLe Windsor & 命令模式

c# - 值不能为空 - 应用程序管理器

c# - WPF 中的任务锁定 UI

java - Android JSON 访问缩略图元素

java - nullColumnHack 是什么意思?

algorithm - 可以用 2x1x1 block 的 2x2 底座构建 2^n 高的塔

ruby - 查找二叉树的叶子