Javascript 为什么使用哈希搜索字符串的函数不起作用?

Javascript 为什么使用哈希搜索字符串的函数不起作用?,javascript,string,debugging,hash,Javascript,String,Debugging,Hash,我写了一个函数,它有两个字符串,s和a,它应该返回a在s中第一次出现的位置。它可以将a作为一个字符使用,但如果第一次出现在s中的第三个字符之后,它将停止工作 我已经检查了mul和add是否工作,a的散列是否正确,我已经将基数减少到10和100(这对散列不是很好,因为它们不是素数),并且它工作(在长度为20的字符串上)。这可能意味着模不能按预期工作 function getIndex(s, a) { // 2 bases and 2 mods to reduce the number o

我写了一个函数,它有两个字符串,s和a,它应该返回a在s中第一次出现的位置。它可以将a作为一个字符使用,但如果第一次出现在s中的第三个字符之后,它将停止工作

我已经检查了mul和add是否工作,a的散列是否正确,我已经将基数减少到10和100(这对散列不是很好,因为它们不是素数),并且它工作(在长度为20的字符串上)。这可能意味着模不能按预期工作

function getIndex(s, a) {

    // 2 bases and 2 mods to reduce the number of collisions

    var base1 = 31337;
    var base2 = 31357;

    var mod1 = 1e9 + 7;
    var mod2 = 1e9 + 9;

    //suffix arrays

    var hs1 = new Uint32Array(s.length);
    var hs2 = new Uint32Array(s.length);

    // bases to the power of a.length

    var ba1 = 1;
    var ba2 = 1;

    // hashes for a

    var ha1 = 0;
    var ha2 = 0;

    //operators

    var mul = (x, y, mod) => (x * y) % mod;

    var add = (x, y, mod) => {
        x += y;
        if(x >= mod) x -= mod;
        if(x < 0) x += mod;
        return x;
    }

    //get hash of a and find value of ba1 and ba2

    for(var i = 0; i < a.length; i++) {
        ha1 = add(mul(ha1, base1, mod1), a.charCodeAt(i), mod1);
        ha2 = add(mul(ha2, base2, mod2), a.charCodeAt(i), mod2);

        ba1 = mul(ba1, base1, mod1);
        ba2 = mul(ba2, base2, mod2);
    }

    //make suffix array

    var h1 = 0;
    var h2 = 0;

    for(var i = 0; i < s.length; i++) {
        h1 = add(mul(h1, base1, mod1), s.charCodeAt(i), mod1);
        h2 = add(mul(h2, base2, mod2), s.charCodeAt(i), mod2);

        hs1[i] = h1;
        hs2[i] = h2;
    }

    //Compare hashes of substrings of s (by removing prefix from the current element) with hash of a

    for(var i = a.length - 1; i < s.length; i++) {
        var h1 = hs1[i];
        var h2 = hs2[i];
        if(i >= a.length) {
            console.log(i, i - a.length, h1);
            h1 = add(h1, -mul(hs1[i - a.length], ba1, mod1), mod1);
            h2 = add(h2, -mul(hs2[i - a.length], ba2, mod2), mod2);
        }
        if(h1 == ha1 && h2 == ha2) return i - a.length + 1;
    }

    return -1;
}

通过更多的日志记录,可以清楚地看到您是日志记录的受害者。JS数字只能精确表示52位整数

getIndex(“abcdefgh”,“fg”)
中,搜索的哈希值
ha1
3196477
,基本乘数
ba1
982007569
,在第六次迭代中前缀哈希值为
hs1[6]=73644174
hs1[4]=800389532
。如果将它们放入算术函数中,则结果为
3196441
,以6为关。这是因为
800389532*982007569
大约比的大两个数量级,而在JS中的计算结果为
785988578572367700
,这显然是错误的


您需要选择较小的模和基,或者在所有计算中使用大整数。

您能解释一下哈希函数的用途吗?检索子字符串出现在另一个字符串中的第一个索引与任务有什么关系?这可以简单地用
s.indexOf(a)
来实现。因为在这个例子中,indexOf的复杂度是(n-m)*m(或简化的O(n*m)),但是使用散列(在这个代码中,它的目的是将字符串变成数字)和后缀数组,我认为复杂度是O(n)(1表示从0到m的循环,2表示从0到n的循环)。我还没有解释它的用途,我使用哈希生成了一个表示字符串后缀的数字后缀数组。由于这个哈希函数的属性(如果我使用
(x-len))
th元素从后缀数组中,将其乘以
len
的幂的基数,然后从
x
th元素中减去,我将得到
(x-len)
th元素到
x
th元素的子字符串的散列。我可以非常快地得到每个子字符串的散列,并将其与我正在寻找的字符串的散列进行比较(这是在比较数字,应该也很快)。好的,那么你想比较散列字符串。我认为你的函数仍然应该比较纯文本字符串。你的“后缀数组”似乎存储前缀的散列?我已经将第28行更改为
var mul=(x,y,mod)=>Number((BigInt(x)*BigInt(y))%BigInt(mod))现在它可以工作了,但我不知道BigInt有多快。我发现它的大小只受内存的限制,所以我不确定它有多快(数学运算和转换)。我也不认为减少基数和mod是明智的,因为这会为更多的冲突腾出空间,匹配不同字符串的哈希。@Franx1024您必须处理冲突,冲突总是会发生的。当使用哈希找到可能的解决方案时,您始终需要通过实际比较stri来确认冲突ngs.@Franx1024关于性能,bigint可能相当慢。但是,它们允许您只使用一个大的散列,而不是两个具有不同基和模的小散列,这样您就可以消除所有代码重复。是的,然后您必须针对
indexOf
进行基准测试。我非常确定JS引擎我在这里发现了indexof的O(nm)复杂性,但是当我查看源代码()时,他们似乎也在使用某种后缀数组。
getIndex("abcdefgh", "f") //returns 5
getIndex("abcdefgh", "fg")//returns -1