在java

时间:2016-12-11 16:23:59

标签: java string hash pattern-matching string-hashing

我试图学习如何使用多个散列匹配给定文本字符串中的模式。我在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? 
        }

    }
} 

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

1 个答案:

答案 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等。