我试图学习如何使用多个散列匹配给定文本字符串中的模式。我在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?
}
}
}
问题是:我无法理解上面代码中我在代码中注释掉的一些代码行。这些线路实际上做了什么?
答案 0 :(得分:2)
这是Rabin-Karp字符串搜索算法。该算法不是将模式与文本的每个部分进行比较,而是尝试比较那些文本的散列值以减少计算。
为计算哈希值,它使用rolling hash维护文本的固定宽度窗口(在本例中为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。
哈希(“abr”)=(97×1012)+(98×1011)+(114×1010)= 999,509
在比较模式到文本的第s个窗口之后的匹配阶段(如果P的P和第s个窗口的哈希值相等),我们需要更新哈希值来表示第(s + 1)个窗口T. (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”的第一个“a”添加的数字来计算下一个子串“bra”的散列,从“abr”的散列计算,即97×1012,乘以基数并添加最后一个“胸罩”,即97×1010。像这样:
base old hash old 'a' new 'a'
hash(“bra”)= [101×(999,509 - (97×1012))] +(97×1010)= 1011309
<强>说明强>:
(int)text.charAt(s) - 97
:97是字符'a'的ascii代码,因此此操作将'a'更改为0,'b'更改为1等。