Rabin–Karp Algorithm
Use rolling polynomial hashes to filter candidate alignments before verifying with exact comparison.
Rolling Hash Idea
Treat substrings of length m as numbers in base d (alphabet size, e.g. 256 for bytes). A hash function maps each length-m window T[s..s+m−1] to an integer. Instead of recomparing all m characters for every shift, update the hash in O(1) when the window slides by one: subtract the contribution of the outgoing character, add the incoming one, modulo a prime q (to keep numbers bounded).
If the hash of the window equals the hash of P, verify with an explicit character-by-character check — hashes can collide (two different strings, same hash).
Algorithm Outline
- Choose base d and modulus q (often a large prime).
- Compute h(P) and the hash of the first window T[0..m−1].
- For each shift s, if window hash equals h(P), compare T[s..s+m−1] to P; on match, record s.
- Roll the hash from s to s+1 using the recurrence for polynomial rolling hash.
Monte Carlo variant: skip verification and accept hash equality (rare false positives). Las Vegas variant: always verify — our description assumes this for correctness.
When Rabin–Karp Helps
Expected time can be O(n + m) when collisions are rare and verifications are few — useful for multiple patterns (compute multiple hashes) or 2D pattern search with 2D rolling hashes.
Worst case remains O(nm) if a malicious text forces many hash matches and full verifications (e.g. all windows hash-collide with P). Good hash parameters and randomization reduce collision probability in practice.
Pseudocode
Below, h is the rolling hash of the current window; t_s denotes the numeric value of window starting at s (mod q). The roll step uses Horner-style updates.
1RABIN-KARP(T, n, P, m, d, q)2 h = d^(m-1) mod q // precompute for rolling3 p = 0 // hash of pattern4 t = 0 // hash of first window5 for i = 1 to m6 p = (d*p + P[i]) mod q7 t = (d*t + T[i]) mod q8 occurrences = []9 for s = 0 to n - m10 if p = t11 if P[1..m] = T[s+1 .. s+m] // verify12 occurrences.append(s)13 if s < n - m14 t = (d*(t - T[s+1]*h) + T[s+m+1]) mod q15 if t < 016 t = t + q // keep non-negative if using mod arithmetic17 return occurrences
Complexity
Preprocessing / first window: O(m) to compute initial hash and pattern hash.
Rolling: O(n − m) updates, O(1) each.
Verifications: O(m) per spurious hash match. Total: O(n + m) average case with few collisions; O(nm) worst case if every window must be verified.
Complexity Analysis
Time Complexity
O(n + m) average; O(nm) worst case
Space Complexity
O(1) beyond input
Worst case if many hash collisions require full verifications