Rolling Hash & Rabin–Karp

Comparing two strings costs O(m); comparing two numbers costs O(1). Rolling hashes turn every window of a text into a number you can update in constant time, which powers plagiarism checkers and duplicate file finders.

intermediate⏱ 12 min read
01

Learn

The idea, the mechanics and the cost.

01A string as a number

You met hash functions in the hash table lesson: turn a key into a number, then use the number. For strings the standard choice is the polynomial hash. Read the string as digits of a number in base p:

h(s) = s[0]·p^(m−1) + s[1]·p^(m−2) + … + s[m−1] (mod M)

With A=1, B=2, p = 31 and M = 97, the string ABAB becomes 27. Equal strings always get equal hashes. Different strings usually get different hashes.

You compute it left to right with h = h·p + code(c), taking mod M at each step so the numbers stay small.

ABAB"ABAB"1× 31³2× 31²1× 31¹2× 31⁰codeweight29791 + 1922 + 31 + 2 = 31746mod 9727← fingerprint

02Rolling the window in O(1)

Rabin–Karp hashes the pattern once, then slides a window of length m over the text. Recomputing each window from scratch would cost O(m) again. The trick is that neighbouring windows share m − 1 characters:

  • remove the leading character: subtract code(first)·p^(m−1)
  • shift everything one place: multiply by p
  • add the new character: add code(next)

That is three arithmetic operations per step, whatever the length of m. Precompute p^(m−1) mod M once. Watch out for negative numbers after the subtraction: add M before taking the remainder.

old window: 27new window: 80ABABCABAB− drop A+ add Cnew = (old − 1·31³) · 31 + 3 (mod 97)(27 − 12) · 31 + 3 = 468 ≡ 80

03Collisions: always verify

A hash squeezes many possible strings into M values, so two different strings will sometimes share a hash. That is a collision. With M = 97, AA and DE both hash to 32.

So when a window's hash equals the pattern's hash, Rabin–Karp compares the actual characters before reporting a match. A false alarm costs O(m) but never gives a wrong answer.

In real code use a large prime like M = 10⁹ + 7 with a random base p, or two different moduli at once. Collisions then become so rare that the expected running time is O(n + m). The same rolling idea also answers "are these two substrings equal?" in O(1) after O(n) preprocessing of prefix hashes.

"AA"1·31 + 1 = 32"DE"4·31 + 5 = 129 ≡ 3232same hash (mod 97)compare the characters:AA ≠ DE → spurious hitmod ≈ 10⁹: collisions are rare, not impossible

Cost at a glance

Hash the patternO(m)
Roll one windowO(1)
Rabin–Karp, expectedO(n + m)
Substring equality after prefix hashesO(1)

Remember

  1. A polynomial hash reads the string as a base-p number modulo M, so equal strings get equal fingerprints.
  2. Rolling the window (remove first, multiply, add next) updates the hash in O(1) per shift.
  3. Equal hashes only suggest a match: verify the characters, and use a large modulus to keep collisions rare.
02

Play

Step through the algorithm, then try it on your own input.

👀 What to watch: Compare the two numbers in the header: characters are only checked when the window hash equals the pattern hash.

Interactive visualizerfocus here, then space ← →
✎ Your inputLetters A–Z; text up to 16, pattern up to 6. Hashes use p = 31, mod 97.

Rabin–Karp (rolling hash) · pattern hash: 27

ABABCABAB
Build the pattern hash char by char: fold in 'A' → 1 (mod 97).

Pseudocode

 1 patHash = Σ code(pat[i]) · p^(k-1-i)  mod m 2 windowHash = hash of text[0..k-1] 3 if windowHash == patHash: verify chars (guard against collisions) 4 else: not a match here 5 roll to next window in O(1): drop leading char, shift, add next
1 / 1
03

Check

Three questions. Pick an answer to see why.

Q1

With A=1, B=2, p = 10 and no modulus, what is the hash of "BAB"?

Q2

The window hash equals the pattern hash. What should Rabin–Karp do?

Q3

Why is rolling to the next window O(1) and not O(m)?

04

Practice

Real problems to lock it in, easiest first.