Algorithm Search⌘K

Proofs: Rabin-Karp algorithm

These are the human proofs of Rabin-Karp algorithm, at full length.

Rabin-Karp outputs exactly the occurrences of the pattern full

This proof shows: correctness, termination.

Claim. For every text T[0…n−1]T[0 \ldots n-1], every pattern P[0…m−1]P[0 \ldots m-1] and every value of the base rr drawn at line L3, RABIN-KARP stops and outputs, in increasing order, exactly the shifts ss with 0≤s≤n−m0 \le s \le n - m and T[s…s+m−1]=PT[s \ldots s+m-1] = P (all ss from 00 to nn when m=0m = 0).

Notation. All hash values are in {0,…,p−1}\{0, \ldots, p-1\}. For a string xx of length ℓ\ell, h(x)=∑i=0ℓ−1c(xi) rℓ−1−i mod ph(x) = \sum_{i=0}^{\ell-1} c(x_i)\, r^{\ell-1-i} \bmod p, and ts=h(T[s…s+m−1])t_s = h(T[s \ldots s+m-1]) for 0≤s≤n−m0 \le s \le n - m.

Lemma 1 (Horner's rule). After line L6, hp=h(P)hp = h(P) and ht=t0ht = t_0.

Proof. Let viv_i be the value of hphp after ii passes, with v0=0v_0 = 0. By induction on ii, vi=∑j=0i−1c(P[j]) ri−1−j mod pv_i = \sum_{j=0}^{i-1} c(P[j])\, r^{i-1-j} \bmod p: the case i=0i = 0 is the empty sum, and vi+1=vir+c(P[i])=∑j=0i−1c(P[j]) ri−j+c(P[i])=∑j=0ic(P[j]) ri−jv_{i+1} = v_i r + c(P[i]) = \sum_{j=0}^{i-1} c(P[j])\, r^{i-j} + c(P[i]) = \sum_{j=0}^{i} c(P[j])\, r^{i-j} modulo pp. For i=mi = m this is h(P)h(P). The same argument with T[0…m−1]T[0 \ldots m-1] gives ht=t0ht = t_0. □\square

Lemma 2 (the window update). For 0≤s<n−m0 \le s < n - m, ((ts−c(T[s]) R) r+c(T[s+m])) mod p=ts+1\big((t_s - c(T[s])\, R)\, r + c(T[s+m])\big) \bmod p = t_{s+1}, where R=rm−1 mod pR = r^{m-1} \bmod p.

Proof. Work in the ring of integers modulo pp. By definition ts=∑j=0m−1c(T[s+j]) rm−1−jt_s = \sum_{j=0}^{m-1} c(T[s+j])\, r^{m-1-j}, and the term with j=0j = 0 is c(T[s]) rm−1=c(T[s]) Rc(T[s])\, r^{m-1} = c(T[s])\, R. So

(ts−c(T[s]) R) r+c(T[s+m])=∑j=1m−1c(T[s+j]) rm−j+c(T[s+m]).(t_s - c(T[s])\, R)\, r + c(T[s+m]) = \sum_{j=1}^{m-1} c(T[s+j])\, r^{m-j} + c(T[s+m]).

Put j=k+1j = k + 1. The sum becomes ∑k=0m−2c(T[s+1+k]) rm−1−k\sum_{k=0}^{m-2} c(T[s+1+k])\, r^{m-1-k}, and the last term is the term k=m−1k = m - 1 of ∑k=0m−1c(T[s+1+k]) rm−1−k=ts+1\sum_{k=0}^{m-1} c(T[s+1+k])\, r^{m-1-k} = t_{s+1}. The reduction modulo pp returns the representative in {0,…,p−1}\{0, \ldots, p-1\}, which is ts+1t_{s+1}. □\square

Lemma 3 (loop invariant). If 1≤m≤n1 \le m \le n, then at line L8 in the pass with shift ss, ht=tsht = t_s and hp=h(P)hp = h(P).

Proof. By induction on ss. For s=0s = 0, this is Lemma 1. Line L4 sets R=rm−1 mod pR = r^{m-1} \bmod p, and no line changes hphp or RR after line L6. If the claim holds for s<n−ms < n - m, then line L10 is true and line L11 sets htht to the value of Lemma 2, which is ts+1t_{s+1}. □\square

Lemma 4 (the test at one shift). If 1≤m≤n1 \le m \le n and 0≤s≤n−m0 \le s \le n - m, the pass for ss outputs ss if and only if T[s…s+m−1]=PT[s \ldots s+m-1] = P.

Proof. If T[s…s+m−1]=PT[s \ldots s+m-1] = P, then the two strings are the same, so ts=h(P)t_s = h(P), and by Lemma 3 the test of line L8 is true. Line L9 compares the window with PP character by character, finds them equal, and outputs ss. If T[s…s+m−1]≠PT[s \ldots s+m-1] \ne P, then either the test of line L8 is false, or it is true and line L9 finds a position where the window and PP differ. In both cases nothing is output for ss. The pass for ss outputs no other shift. □\square

Termination. Each loop runs over a finite range of integers: line L6 runs mm times, line L7 runs n−m+1n - m + 1 times, and the comparison of line L9 looks at most at mm positions. So the algorithm stops.

Theorem. The claim holds.

Proof. If m=0m = 0, line L1 outputs 0,1,…,n0, 1, \ldots, n, and the empty pattern occurs at exactly these shifts. If m>nm > n, no shift ss satisfies 0≤s≤n−m0 \le s \le n - m, and line L2 stops with no output. Otherwise, line L7 visits each ss from 00 to n−mn - m once, in increasing order, and by Lemma 4 it outputs ss exactly when PP occurs at ss. The proof does not use any property of rr or of pp, so the output is correct for every base. ■\blacksquare

Remark (where randomness enters). The choice of rr affects only how often line L9 runs on a window that is not an occurrence. For a fixed TT and PP and a shift ss that is not an occurrence, the window and PP are distinct strings of the same length mm, and by the polynomial hash collision bound Pr⁡r[ts=h(P)]≤(m−1)/p\Pr_r[t_s = h(P)] \le (m-1)/p when pp is prime and rr is uniform on {0,…,p−1}\{0, \ldots, p-1\}. This gives the expected time on the main page.

Back to Rabin-Karp algorithm