3.8.3 · D4String Algorithms

Exercises — Rabin-Karp — rolling hash, O(n+m) expected

2,101 words10 min readBack to topic

Before we start, we fix one shared convention so no symbol is ever unexplained:

The figure below is the one picture the whole page leans on — keep it in view.

Figure — Rabin-Karp — rolling hash, O(n+m) expected

Level 1 — Recognition

L1.1 — Spot the digit weights

For the string "cat" (so ) with base , write the polynomial for its hash before any mod. Which character carries the weight ?

Recall Solution

WHAT: we expand term by term. WHY: the leftmost character is the most significant digit, so it multiplies the highest power of . Compute: , plus , plus . The first character c carries .

L1.2 — What does a hash mismatch prove?

You compute and a window's hash . What can you conclude, and what must you do?

Recall Solution

Conclude: the strings are definitely different. Equal strings always produce equal hashes, so unequal hashes force unequal strings. Do: skip this window instantly — no character check needed. (Contrast: a hash match only means "probably equal," so it needs verification.)


Level 2 — Application

L2.1 — Compute a hash mod

Compute with .

Recall Solution

From L1.1 the raw value is . WHY mod: we keep it small and bounded. , since and .

L2.2 — One roll by hand

Text (), , , . You already have . Compute , the hash of window "ats", using Drop, Slide, Add.

Recall Solution

Precompute . Since , . Drop leading c: . Keep non-negative: . Slide () and Add trailing s: . , so . Check by direct formula: ; . ✓ The roll and the direct computation agree — that is the whole point.

L2.3 — Find the match position

Search in , , , . Report every index of a match.

Recall Solution

. Window 0 "aa" → skip. Roll to window 1 "ab": . . Matches → verify → "ab"=="ab" ✓. Match at index 1.


Level 3 — Analysis

L3.1 — Why a tiny prime is dangerous

With and , hash the two-letter strings "ac" () and "ba" (). Do they collide? Explain the consequence for running time.

Recall Solution

. . They collide () even though the strings differ. Consequence: with a small , many windows produce equal hashes, so we run the verification constantly → the "expected " collapses toward the worst case . This is exactly why we choose a large prime like . See Modular Arithmetic and Birthday Paradox.

L3.2 — Collision probability reasoning

With a good random hash mod prime , the chance a single wrong window collides is about . If a text has windows and , roughly how many spurious verifications do we expect?

Recall Solution

WHY this tool: expected count (number of trials) (per-trial probability), the linearity-of-expectation shortcut. About one in a thousand whole searches does even a single wasted verification — negligible. That is why total expected cost stays .

L3.3 — Construct a worst case

Give a text/pattern family (with a chosen ) that forces . Explain why.

Recall Solution

Take ( copies) and ( copies). Every window equals , so every hash matches and every window triggers a full verification. Total . Note this is a genuine-match worst case; an adversarial collision worst case instead uses a known small and crafts distinct windows that all hash to . Both defeat the average-case bound. This is why Knuth-Morris-Pratt (guaranteed ) is preferred when adversaries are possible.


Level 4 — Synthesis

L4.1 — Two-hash defence

You use two independent hashes and with , . A wrong window collides on both only if it collides on each. Estimate the combined per-window false-positive rate and compare to a single hash mod .

Recall Solution

WHY independence multiplies: two independent events both happen with probability equal to the product. Combined rate . A single hash mod gives . So two small primes () already beat one modulus of : . Lesson: two independent moduli multiply their protection, which is why double hashing is so strong. See String Hashing for Substring Comparison.

L4.2 — Full trace over a real text

Search in , , , . Trace all windows and list match indices.

Recall Solution

. . Codes of : .

  • Window 0 "ba": . → skip.
  • Roll → Window 1 "ab": drop b: ; slide+add b: . → verify "ab" ✓. Match at 1.
  • Roll → Window 2 "ba": drop a: ; slide+add a: . → skip.
  • Roll → Window 3 "ab": drop b: ; slide+add b: . → verify ✓. Match at 3. Matches: indices 1 and 3. (Direct check: babab[1..2]=ab, [3..4]=ab. ✓)

Level 5 — Mastery

L5.1 — Design a robust searcher

You must search a pattern in a text supplied by an untrusted user (possible adversary), needing near-guaranteed correctness and good speed. State your full design and justify each choice against the failure modes you learned.

Recall Solution

Design:

  1. Use double hashing: two random large primes , random bases . Why: combined collision rate — even a crafted adversary cannot predict the random bases, so L3.3's collision attack fails.
  2. Precompute once for each hash. Why: recomputing inside the loop makes each roll and kills the speedup (the L2 precompute mistake at scale).
  3. Guard subtraction with ((x)%q+q)%q. Why: prevents negative-hash corruption (L2 trap).
  4. Still verify char-by-char on a (double) hash hit. Why: keeps correctness exact, and with double hashing verifications are essentially only on true matches, so expected time stays .
  5. Fallback: if guaranteed worst-case is mandated (e.g. hard real-time), switch to Knuth-Morris-Pratt or Z-Algorithm, which need no randomness.

L5.2 — Prove the roll algebraically

Show that the Drop-Slide-Add formula produces exactly , ignoring mod. Where do we actually need mod to make it valid?

Recall Solution

Write . The leading term is . Drop: . Slide (): . Re-index with : — this is the hash of but missing its last character and with each weight one power too high... precisely, it equals the first terms of . Add : fills the final low-order digit, giving . ∎ Mod validity: addition, subtraction, and multiplication all commute with "" — that is a theorem of Modular Arithmetic. So doing every step mod gives the same remainder as computing the giant number first and reducing at the end. Division would not survive mod, which is why our hash uses only .


Recall One-line self-test

Rolling update formula ::: Why keep verification after a double-hash hit? ::: To stay exactly correct; the rare/adversarial collision is the only thing between "probably equal" and "equal."

Connections

  • Rabin-Karp — rolling hash, O(n+m) expected — the parent theory these drills exercise
  • Hashing — polynomial/modular hash foundations used throughout
  • Modular Arithmetic — why survive mod but division is fragile (L5.2, L2 traps)
  • Birthday Paradox — collision-probability intuition behind L3.2
  • Knuth-Morris-Pratt, Z-Algorithm — worst-case-guaranteed fallbacks (L3.3, L5.1)
  • String Hashing for Substring Comparison — the double-hashing idea from L4.1