0

我试图学习如何使用多重散列来匹配给定文本字符串中的模式。我在java中找到了以下实现:

void multiHashing() {
    int counter = 0;
    int d = 26;
    int r = 10;
    int [] qP = qPrimes(d,r); // stores 10 prime numbers
    long [] P = new long[r];
    long [] T = new long[r];
    long [] H = new long[r];
    for (int k=0;k<r;k++) {
        H[k] = mod(((long) Math.pow(d, m-1)), qP[k]);
        for (int i=0;i<m;i++) {
            P[k] = mod((d*P[k] + ((int)pattern.charAt(i) - 97)), qP[k]); //what has been done here
            T[k] = mod((d*T[k] + ((int)text.charAt(i) - 97)), qP[k]);
        }           
    }
    for (int s=0;s<=n-m;s++) {
        if (isEqual(P,T)) {
            out.println(s);
            counter++;
        }
        if (s < n-m) {
            for (int k=0;k<r;k++)
                T[k] = mod(d*(T[k] - ((int)text.charAt(s) - 97)*H[k]) + ((int)text.charAt(s+m) - 97), qP[k]);       // what has been done here? 
        }

    }
} 

问题是:我无法理解上面代码中我在代码中注释掉的某些行。这些行实际上做了什么?

4

1 回答 1

2

这是Rabin-Karp字符串搜索算法。该算法不是将模式与文本的每个部分进行比较,而是尝试比较它们的哈希值以减少计算量。

为了计算散列值,它使用滚动散列来维护文本的固定宽度窗口(在这种情况下width = length of pattern),并通过一次移动一个字符来更新该窗口。

Input: pattern P, text T, d, prime number q

m = P.length
n = T.length
p = 0 // hash of pattern P
t = 0 // hash of text T
h = (d ^ (m-1)) % q 

// preprocessing: hashing P[1..m] and T[1..m] (first window of T)
for i = 1 to m 
    p = (d * p + P[i]) % q //(1)
    t = (d * t + T[i]) % q

// matching
for s = 0 to n-m
    if(p == t)
        if(P[1..m] == T[s+1..s+m]
            print "matched"
    // update the rolling hash
    if(s < n-m)
        t = (d * (t - T[s+1] * h) + T[s+m+1]) % q // (2)

在预处理阶段,它计算模式 P 的哈希值和文本 T 的第一个窗口。为了计算模式的哈希值,我们需要计算每个字符的哈希值。 (1) p = (d * p + P[i]) % q实际上计算第 i 个字符的哈希值。

来自维基百科的例子:

// ASCII a = 97, b = 98, r = 114。

hash("abr") = (97 × 1012) + (98 × 1011) + (114 × 1010) = 999,509

在将模式与文本的第 s 个窗口进行比较之后的匹配阶段(如果 P 的哈希值与 T 的第 s 个窗口的哈希值相等),我们需要更新哈希值以表示 T 的第 (s+1) 个窗口。(2) t = (d * (t - T[s+1] * h) + T[s+m+1]) % q首先减去最后一个窗口的第一个字符的哈希值,然后添加下一个字符的哈希值,从而将窗口向前移动一个字符。

来自维基百科:

滚动哈希函数只是将子字符串中每个字符的值相加。这个滚动哈希公式可以在恒定时间内从前一个值计算下一个哈希值:s[i+1..i+m] = s[i..i+m-1] - s[i] + s[i+m]

然后,我们可以从“abr”的哈希中计算下一个子字符串“bra”的哈希,方法是减去为“abr”的第一个“a”添加的数字,即 97 × 1012,乘以基数并添加“bra”的最后一个a,即97×1010。像这样:

           base  old hash  old 'a'     new 'a'

哈希(“胸罩”)= [101 × (999,509 - (97 × 1012))] + (97 × 1010) = 1,011,309

备注

(int)text.charAt(s) - 97: 97 是字符 'a' 的 ascii 代码,所以这个操作将 'a' 变为 0, 'b' 变为 1,等等。

于 2016-12-12T05:58:20.117 回答