c- Karp-Rabin 滚动哈希 - 跳过和附加部分

问题描述

我的 Karp-rabin 算法的特定部分需要一点帮助。 我想要做的是用固定的 sliding window 和单独的 appendskip 部分实现版本。 Sliding window 工作得很好。当我尝试将整体 sliding window 拆分为 appendskip 部分时会出现问题。 Append 似乎工作正常,但 skip 是最近几天让我头疼的问题。

问题 - 我正在浏览包含一些订阅模式实例的字符串。 Sliding window 检测到它,但没有检测到其他两个。

这个想法是 RH 结构保存了 (base ^ window size) mod prime number (b2wmod) 的预计算值,所以我可以删除前导字符串的字符。此值在所有 appendskip 之后随着窗口大小的变化而变化。为了减小b2wmod的值,乘法逆被用于不处于模删除的情况(基模模值的逆)。它也是预先计算好的。

以下是我感兴趣的部分代码。我不会发布整个代码,以免您阅读所有内容,但可以在必要时上传。乘法逆似乎计算正确,但我也可以上传代码

希望得到任何帮助!提前致谢!

void
append_to_rh(RH rh)
{
    uint64_t hash    = rh->hash;
    uint64_t base    = rh->base;
    uint64_t mod     = rh->mod;
    uint64_t b2wmodm = rh->b2wmodm;
    char     new     = rh->new;
    
    hash             = ( hash * base + new ) % mod;    
    b2wmodm          = ( b2wmodm * base ) % mod;
    
    rh->hash         = hash;
    rh->b2wmodm      = b2wmodm;
}

void
skip(RH rh)
{
    uint64_t hash       = rh->hash;
    uint64_t base       = rh->base;
    uint64_t mod        = rh->mod;
    uint64_t b2wmodm    = rh->b2wmodm;
    uint64_t m_inv      = rh->m_inv;
    char     old        = rh->old;
    uint64_t correction = old * mod;
   
    b2wmodm = ( b2wmodm * (m_inv % mod) ) % mod;
    hash    = ( hash - old * b2wmodm + correction ) % mod;

    rh->hash    = hash;
    rh->b2wmodm = b2wmodm;
}

void
slide_window(RH rh)
{
    uint64_t base    = rh->base;
    uint64_t mod     = rh->mod;
    uint64_t hash    = rh->hash;
    uint64_t b2wmodm = rh->b2wmodm;
    char old = rh->old;
    char new = rh->new;

    hash     = ( hash * base - old * b2wmodm + new ) % mod;
    rh->hash = hash;
}

解决方法

您的 appendskip 函数工作正常。这是我用于测试的示例代码

#include <string>
#include <cassert>

typedef long long ll;

ll hash,b2wmodm,base,inv_base,mod;

// fast exponentiation,for calculating inv_base
ll exp(ll a,ll b){
    ll ans = 1;
    while (b){
        if (b&1){
            ans *= a;
            ans %= mod;
        }
        a *= a;
        a %= mod;
        b >>= 1;
    }
    return ans;
}

// calculates expected hash of the string
ll expected_hash(std::string s) {
    ll result = 0;
    ll multiplier = 1;
    for (int i = s.length()-1; i >= 0; i--) {
        result += s[i] * multiplier % mod;
        result %= mod;
        multiplier = multiplier * base % mod;
    }
    return result;
}

// same as your append and skip functions
void append_to_rh(ll newc) {
    hash = (hash * base + newc) % mod;
    b2wmodm = (b2wmodm * base) % mod;
}

void skip(ll old) {
    ll correction = old * mod;

    b2wmodm = (b2wmodm * (inv_base % mod)) % mod;
    hash = (hash - old * b2wmodm + correction) % mod;
}

int main() {

    base = 29;
    mod = 1000000007;

    hash = 0;
    b2wmodm = 1;
    inv_base = exp(base,mod-2);

    srand(time(nullptr));
    std::string s;
    for (int i = 0; i < 2000; i++) {
        if (i < 1000 || rand()%2) {
            char newchar = rand()%26 + 'a';
            s += newchar;
            append_to_rh(newchar);
            assert(expected_hash(s) == hash);
        } else {
            char oldchar = s[0];
            s = s.substr(1,s.length());
            skip(oldchar);
            assert(expected_hash(s) == hash);
        }
    }
}

我猜你代码的其他部分会导致问题。也许您正试图跳过一个空窗口,或者您使用的是非主要 mod。