3.8.3 · D1String Algorithms

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

2,454 words11 min readBack to topic

Before you can read the parent note you must own eight small ideas. We introduce them in order: each one uses only the ones before it. Nothing here assumes you have seen strings-as-numbers, modular arithmetic, or hashing before.


1. A string and its characters

Picture it. Look at the top row of the figure below: the word a b c sits in boxes numbered . That box number is called the index.

Figure — Rabin-Karp — rolling hash, O(n+m) expected
  • = the whole string.
  • = the character sitting in box number .
  • The very first character is , not . Programmers count from zero — get used to it now, because every formula in the parent note does.

Why the topic needs this. The hash formula reads the string one box at a time, from the very first character onward. If you don't know what means, the whole formula is noise. (How many boxes there are — the length — we name in step 2.)


2. Length symbols and

Picture it. In the figure above, the long strip is (length ) and the short strip is (length ). We slide along looking for a spot where they match.

Now we can pin down step 1 precisely: the pattern's characters run from up to — exactly boxes.

Why the topic needs this. The whole promise of Rabin-Karp is speed measured as "" — you literally cannot read that promise without knowing what and count. (What "" means comes in step 8.)


3. Character codes — turning letters into digits

In the parent's tiny example the codes were . In real code they use each letter's ASCII value (e.g. a = 97). The exact scheme doesn't matter; what matters is that each character now is a number we can compute with.

Picture it. The bottom row of the figure in step 1 shows each letter replaced by its code number, sitting under its box.

Why the topic needs this. The polynomial hash treats each as a number. That only makes sense once every character has a code.


4. A "window" sliding over the text

Picture it. Below, a red frame of width sits on the text. Slide it one box to the right and you get the next window.

Figure — Rabin-Karp — rolling hash, O(n+m) expected
  • = the characters .
  • The two dots .. mean "from here to there, inclusive."

How many windows are there? If the pattern fits (), the frame can start at index and its last legal start is , giving positions. If the pattern is longer than the text (), the frame simply doesn't fit anywhere, so there are zero windows and the answer is "no match" before we even hash. Always check this edge case first.

Why the topic needs this. Rabin-Karp compares the pattern against one window at a time. The rolling-hash magic is all about updating cheaply when this red frame slides by one.


5. Base- place value — the heart of the hash

Picture it. The figure below stacks a decimal number and a string side by side. Under each slot sits its multiplier: for the string; for the decimal. Same skeleton, different base.

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

So for a string of length we write its raw polynomial value as a capital :

Read it slowly. The first character is multiplied by the biggest power ; the last character is multiplied by . This is why swapping two letters usually changes the number — position carries weight.

Why the topic needs this. This polynomial is the raw hash value . Everything downstream — the rolling update, the collision talk — is arithmetic on this one expression. In step 6 we shrink with a mod to get the value we actually store. See Hashing and String Hashing for Substring Comparison for where this same idea reappears.


6. Modular arithmetic — clock math and the prime

Picture it. The clock figure below has marks. Counting past the top wraps around — that wrap-around is exactly what "mod" does. The red hand shows where lands on a clock: at .

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

Three facts we lean on — the first two justify shrinking at every step, the third keeps us positive:

  • Add-mod: . You may take the mod early on each piece and still get the right final answer.
  • Multiply-mod: . This is the one that makes the rolling hash legal: the roll multiplies by the base , and this rule says we can keep everything reduced mod through that multiplication without the numbers ever blowing up.
  • Subtraction can go negative (e.g. ). On a clock, is the same spot as . So to keep the result in the range we compute, in code, ((x) % q + q) % q — take the remainder, nudge it up by so it can't be negative, then take the remainder again.

Why the topic needs this. Without the numbers are unstorable. With a tiny you get collisions everywhere (a parent-note mistake!). The prime is the dial that trades collision-safety against number size, and the multiply-mod rule is what makes the roll possible at all.


7. Hash and collision

The golden rule this forces:

  • Different hashes strings are definitely different (safe to skip).
  • Same hashes strings are probably equal — you must verify character by character.

Why the topic needs this. Verification is not optional decoration; it is the safety net that turns "probably a match" into "certainly a match."


8. Big-O notation

Picture it in your head. is a flat line, is a straight slope, is a steep curve. Rabin-Karp's whole selling point is turning the naive into an expected .

Why the topic needs this. Every claim in the parent note — " update", " expected", " worst case" — is written in this language. The rival methods Knuth-Morris-Pratt and Z-Algorithm are compared using the same yardstick.


How these feed the topic

Strings and index s_i

Character codes as numbers

Lengths n and m

Sliding window of size m

Base-b polynomial H of s

Base b and place value

Reduce mod prime q to get h of s

Hash as fingerprint

Collisions force verify

Rolling update in O of 1

Multiply-mod rule

Big-O notation

O of n plus m expected

Rabin-Karp

Follow the arrows into the parent topic and every symbol there will already be yours.


Equipment checklist

Cover the right side and see if you can answer each before revealing.

What does mean and where does counting start?
The character in box of string ; counting starts at , so a length- string has boxes to .
What do and count?
= length of the text ; = length of the pattern .
Why must characters become numbers first?
A computer can only do arithmetic on numbers, so each character gets a numeric code before hashing.
What is a window and how many are there?
A stretch of consecutive text characters ; there are of them when , and when .
In the base- hash, which character gets the biggest multiplier?
The first character , multiplied by ; the last gets .
What is ?
— any base to the power zero equals one.
What is the difference between and ?
is the raw base- polynomial (possibly huge); is that value shrunk onto the clock of size — the fingerprint we store.
What does give you, and how is it written in code?
The remainder of divided by (range to ); in code the same thing is written x % q.
Which mod rule makes the rolling update legal?
Multiply-mod: , so we can keep values reduced through the roll's multiply-by-.
How do you keep a hash non-negative after subtraction?
Add before the final mod: ((x) % q + q) % q.
Why use a large prime for ?
It spreads hash values evenly and keeps collisions rare while bounding number size.
What is a collision and what does it force you to do?
Two different strings sharing a hash; it forces a character-by-character verification on every hash match.
What does versus mean?
= fixed steps regardless of input size (instant); = work proportional to roughly one pass over each string.