大字符串的 Rabin Karp 算法

Posted

技术标签:

【中文标题】大字符串的 Rabin Karp 算法【英文标题】:Rabin Karp algorithm for big strings 【发布时间】:2016-05-10 23:00:23 【问题描述】:

我为子字符串搜索写了一个简单的Rabin-Karp算法的分步实现,它似乎工作正常,直到哈希变得大于模数,然后就出错了......

这是代码,很简单:

typedef long long ll;

#define B 257
//base
#define M 2147483647
//modulus

//modulus for positive and negative values
ll mod(ll a)
    return (a % M + M) % M;


//fast way to calculate modular power
ll power(ll n, ll e)
    ll r = 1;
    for(; e > 0; e >>= 1, n = (n*n) % M)
        if(e&1) r = (r * n) % M;
    return r;


//function to calculate de initial hash
//H(s) = s[0] * B^0 + s[1] * B^1 + ...
ll H(char sub[], int s)
    ll h = 0;
    for(ll i = 0; i < s; i++)
        h = mod(h + mod(power(B, i) * sub[i]));
    return h;


//brute force comparing when hashes match
bool check(char text[], char sub[], int ini, int s)
    int i = 0;
    while(text[ini + i] == sub[i] && i < s) i++;
    return i == s;


//all together here
void RabinKarp(char text[], char sub[])
    int t = strlen(text), s = strlen(sub);
    ll hs = H(sub, s), ht = H(text, s);
    int lim = t - s;

    for(int i = 0; i <= lim; i++)
        if(ht == hs)
            if(check(text, sub, i, s))
                printf("MATCH AT %d\n", i);           

        ht -= text[i];      
        ht /= B;            
        ht = mod(ht + power(B, s - 1) * text[i + s]);

        //we had    text[i] * B^0 + text[i+1] * B^1 + ... + text[i + len - 1] * B^(len-1)

        //then    text[i+1] * B^1 + text[i+2] * B^2 + ... + text[i + len - 1] * B^(len-1)
        //then    text[i+1] * B^0 + text[i+2] * B^1 + ... + text[i + len - 1] * B^(len-2)
        //finally we add a new last term text[i + len] * B^(len-1)

        //so we moved the hash to the next position
    




int main()
    char text[] = "uvauvauvaaauva";
    char sub[] = "uva";
    char sub2[] = "uvauva";
    RabinKarp(text, sub);
    printf("----------------------------\n");
    RabinKarp(text, sub2);

问题是,在我取模后,散列可能会变成一个小数,然后,当我向它添加一些大因素时,即使应该匹配,散列也可能不匹配。

例如:xabc里面的abc

当我取abc和xab的hash时,假设它们都大于模数,所以模数运算后它们变小了。

然后,当我删除“x”并添加“c”因子时,总和可以小于模数但仍然很大,所以它不会匹配。

我该如何克服这个问题?

【问题讨论】:

对您的函数运行测试,以确保它们按照您的预期工作 - 几乎可以肯定您的模数学中存在错误或逻辑错误 假设我的 mod 是 2550 和 base 50。如果我搜索 aaa,它应该是 1 + 50 + 2500 = 2551 % 2550 = 1。如果我的字符串是 baaa,第一个哈希是 2 + 50 + 2500 = 2552 % 2550 = 2,然后当我减去 2 然后除以 50 时,它保持为 0,当我将 2500 相加时,我的哈希将是 2500,而它应该是 1 【参考方案1】:

ht /= B; 是不合理的。首先,因为您正在做算术 mod M,并且除法的模等效与标准模等效。其次,因为您应该期望 x 和 x + M 得到相同的答案,但事实并非如此。

你有 text[i] * B^0 + text[i+1] * B^1 + ... + text[i + len - 1] * B^(len-1)

如果你和你一起工作

text[i] * B^(len-1) + text[i+1] * B^(len - 2) + ... + text[i + len - 1] * B^0

你可以减去 text[i] * B^(len-1) 然后乘以 B 代替

【讨论】:

是的,关于除法不合理的部分我记得需要用模逆来代替(如果我错了,请纠正我) 您可以使用模逆,但您不必这样做。如果您按照我的回答反转多项式的构造方式,那么您想要摆脱的角色的影响与 B 的最高功率相关联,您可以将其减去,然后将剩下的乘以 B 以移动所有内容上升一个位置,然后添加新角色。 哦,我明白了,有趣的方法! 我计算了 257 模数 2147483647 的模逆并乘以而不是除,但它没有用 :( 但使用 @mcdowella 方法它工作得很好!谢谢!!!

以上是关于大字符串的 Rabin Karp 算法的主要内容,如果未能解决你的问题,请参考以下文章

字符串字符串查找 ( Rabin-Karp 算法 )

算法导论字符串匹配—朴素算法Rabin-Karp有限自动机KMP

模式匹配:滚动哈希到 Rabin-Karp 算法

模式匹配:滚动哈希到 Rabin-Karp 算法

何时使用 Rabin-Karp 或 KMP 算法?

Rabin Karp 字符串匹配算法