Visual walkthrough — Rabin-Karp — rolling hash, O(n+m) expected
We build up in this order: a string is a number → that number is a hash → the hash of the next window is the old one with three edits → all edge cases → one summary picture.
Step 1 — A string is just a number written in some base
WHAT. Take a short string, say . Give each letter a number code: (its position in the alphabet). Now we glue those codes together the same way we glue digits to make a decimal number.
WHY. Comparing two numbers is a single machine operation — . Comparing two strings letter by letter is . So the first move is: turn the string into one number. We call that number its hash.
PICTURE. In the figure, (an ordinary decimal number) sits on top; below it, is treated in exactly the same way but in base instead of base .

So for with :
Each symbol is doing a job: are the letter codes (); are the place values that keep the letters from getting mixed up.
Step 2 — Keep the number small with a prime
WHAT. For a long string those powers of explode into astronomically huge numbers. We tame them by working modulo a prime — meaning: after every operation, keep only the remainder when divided by .
WHY. Two reasons, both drawn in the figure. (1) The number must fit in a machine word, so we cap it. (2) Wrapping around at a large prime scatters strings evenly across , so unrelated strings rarely land on the same value. A prime (not any old number) avoids nasty patterns where many strings collapse together — see Modular Arithmetic.
PICTURE. The number line from to is a circular clock. Big values "wrap around"; two arrows landing on the same tick are a collision.

The collision risk is roughly per comparison (the Birthday Paradox explains why it feels smaller than you'd fear). That is why we later verify on a match — a hash is a filter, never a proof.
Step 3 — Line up the pattern against the first window
WHAT. We have a text of length and a pattern of length . A window is a slice of that is exactly letters long, starting at some index : written . We compute once, then the hash of the first window .
WHY. If , the window is definitely not the pattern — skip it for free. If they're equal, it's probably a match, so we do the cheap-in-expectation letter check. We want to test all windows, so the real question of the next steps is: how do we get from one window's hash to the next without redoing the whole sum?
PICTURE. The pattern sits under ; a bracket marks window . An amber bracket shows where window will be — shifted right by one letter.

Every term is a letter code; every is its place value inside this window.
Step 4 — Drop, Slide, Add: the whole trick in one move
WHAT. Write both window hashes on top of each other and stare at what changed.
Everything in the middle is the same letters — but each has moved up one place (its exponent grew by ). Only two things are new: the orange leading term left, the cyan trailing term arrived.
WHY. This is the payoff. Instead of recomputing terms, we perform three tiny edits:
- Drop the leading letter's contribution (subtract it).
- Slide everyone left one place (multiply the whole thing by — that adds to every exponent at once).
- Add the new trailing letter .
That is per window. Multiplying by handles all the middle shifts in a single stroke — that is the clever part.
PICTURE. Three colour-coded arrows: red crosses out the old front letter, a cyan sweep multiplies everything by , an amber box drops in the new letter.

Step 5 — Trace it on a real example (watch the sum stay intact)
WHAT. Search in with . Here , so .
- .
- Window : . Since → skip (no letters compared!).
- Roll to window using the boxed formula:
- → verify the two letters → ✓. Match at index .
WHY. Notice we never re-summed the window from scratch — we edited into with one drop, one slide, one add. That is the step in action.
PICTURE. The text with both windows; hashes and shown, and the pattern hash on the side. Green tick on the winning window.

Step 6 — Edge case: the subtraction goes negative
WHAT. When we compute under mod arithmetic, (already reduced to ) may be smaller than the term we subtract. The raw result is negative — an illegal state for our hash, which must live in .
WHY. In real modular arithmetic , but most programming languages return a negative remainder for the operator. If we leave it negative, later comparisons break silently. The fix is to add a full back before the final mod.
PICTURE. On the clock circle, an arrow steps backwards past into negative territory; a second amber arrow adds to bring it onto the correct positive tick — same point on the circle.

Step 7 — Degenerate windows: , , and the collision case
WHAT & WHY. Three boundary scenarios the reader will eventually hit:
- (pattern as long as text): there is exactly one window. No rolling happens — we just compare with once, then verify. The loop body never fires; the algorithm degrades gracefully to a single check.
- (pattern longer than text): zero windows exist. We must return "no match" before touching the loop — otherwise we index past the end of .
- Collision (different strings, equal hash): the whole reason verification exists. Two distinct windows can land on the same tick of the clock from Step 2. Hashes match → we compare letters → mismatch → we correctly reject. No false match is ever reported.
PICTURE. Three mini-panels: (a) single bracket, (b) pattern overhanging with a red "no window" stamp, (c) two different strings both pointing to one clock tick with a red "verify saves us" arrow.

The one-picture summary
Everything at once: the text as a row of letters, the window as a bracket that hops right one step at a time, each hop labelled Drop · Slide · Add, each landing checked against the clock of residues mod , and a green verify only where hashes coincide.

Total: setup + rolling + a tiny expected verification cost expected.
Recall Feynman retelling of the whole walkthrough
Think of each string as a secret number, built exactly like a decimal number but with letters as digits (Step 1). The numbers get huge, so we spin them around a clock of size — a big prime — to keep them small and spread out (Step 2). To search, we make the pattern's number and the first window's number and compare (Step 3). Now the magic: to get the next window's number, we don't rebuild it — we rub out the front letter, shove everyone one place left by multiplying by the base, and drop the new letter on the end. Drop, Slide, Add — one heartbeat each (Step 4). We watched it work on "aab" and it matched "ab" at index 1 without ever comparing letters on the misses (Step 5). Two gotchas: the subtraction can dip below zero on the clock, so we add a full lap of back (Step 6); and pattern-too-long, pattern-equal-length, and unlucky-collision cases each get handled, with the letter-by-letter double-check as our safety net (Step 7). Add it up: build once, then hop for free — linear time on average.
Recall
Why does multiplying the whole hash by shift every letter?
What are the three edits of a roll, in order?
What must be precomputed once for rolls?
Why can the subtraction step go negative, and the fix?
((x % q) + q) % q.What happens when ?
Why verify even after a hash match?
Connections
- Parent · Rabin-Karp — the algorithm this page derives
- Hashing — why the polynomial shape spreads strings apart
- Modular Arithmetic — the clock, primes, and the non-negative fix
- String Hashing for Substring Comparison — same Drop·Slide·Add for range equality
- Knuth-Morris-Pratt · Z-Algorithm — collision-free linear alternatives
- Birthday Paradox — the intuition behind collision odds