gpt4 book ai didi

c++ - Rolling hash的快速实现

转载 作者:IT老高 更新时间:2023-10-28 22:20:16 25 4
gpt4 key购买 nike

我需要一个滚动哈希来搜索文件中的模式。 (我正在尝试使用 Rabin-Karp string search algorithm )。

我了解一个好的 Hash 如何工作以及一个好的 Rolling Hash 应该如何工作,但我无法弄清楚如何有效地实现 divide (或逆乘法)滚动散列时。我还阅读了 rsync 使用 adler32 的滚动版本,但这看起来不像是一个足够随机的散列。

理想情况下,如果您能指出一个优化的 C/C++ 实现,那就太好了,但是任何指向正确方向的指针都会有所帮助。

最佳答案

Cipher 的“prime base”想法应该可以正常工作 - 尽管他发布的解决方案看起来有点粗略。

我认为这种方法不需要逆乘法。这是我的解决方案:

假设我们当前散列的字符串是“abc”,我们想追加“d”并删除“a”。

就像 Cipher 一样,我的基本哈希算法是:

unsigned hash(const string& s)
{
unsigned ret = 0;
for (int i = 0; i < s.size(); i++)
{
ret *= PRIME_BASE; //shift over by one
ret += s[i]; //add the current char
ret %= PRIME_MOD; //don't overflow
}
return ret;
}

现在,实现滑动:

hash1 = [0]*base^(n-1) + [1]*base^(n-2) + ... + [n-1]

我们想在最后添加一些东西并删除第一个值,所以

hash2 = [1]*base^(n-1) + [2]*base^(n-2) + ... + [n]

首先我们可以添加最后一个字母:

hash2 = (hash1 * PRIME_BASE) + newchar;
=> [0]*base^n + [1]*base^(n-1) + ... + [n-1]*base + [n]

然后简单地减去第一个字符:

hash2 -= firstchar * pow(base, n);
=> [1]*base^(n-1) + ... + [n]

重要提示:您必须小心溢出。您可以选择让它溢出 unsigned int,但我认为它更容易发生冲突(但也更快!)

这是我的实现:

#include <iostream>
#include <string>
using namespace std;

const unsigned PRIME_BASE = 257;
const unsigned PRIME_MOD = 1000000007;

unsigned hash(const string& s)
{
long long ret = 0;
for (int i = 0; i < s.size(); i++)
{
ret = ret*PRIME_BASE + s[i];
ret %= PRIME_MOD; //don't overflow
}
return ret;
}

int rabin_karp(const string& needle, const string& haystack)
{
//I'm using long longs to avoid overflow
long long hash1 = hash(needle);
long long hash2 = 0;

//you could use exponentiation by squaring for extra speed
long long power = 1;
for (int i = 0; i < needle.size(); i++)
power = (power * PRIME_BASE) % PRIME_MOD;

for (int i = 0; i < haystack.size(); i++)
{
//add the last letter
hash2 = hash2*PRIME_BASE + haystack[i];
hash2 %= PRIME_MOD;

//remove the first character, if needed
if (i >= needle.size())
{
hash2 -= power * haystack[i-needle.size()] % PRIME_MOD;
if (hash2 < 0) //negative can be made positive with mod
hash2 += PRIME_MOD;
}

//match?
if (i >= needle.size()-1 && hash1 == hash2)
return i - (needle.size()-1);
}

return -1;
}

int main()
{
cout << rabin_karp("waldo", "willy werther warhol wendy --> waldo <--") << endl;
}

关于c++ - Rolling hash的快速实现,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/711770/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com