3.8.2 · D2String Algorithms

Visual walkthrough — KMP algorithm — failure function, O(n+m) — full derivation

2,392 words11 min readBack to topic

Prerequisite ideas we lean on: Borders and Periods of Strings (a border is a prefix that is also a suffix) and, for the running-time picture, Amortized Analysis. Cousins that solve the same job differently: Z-algorithm, Rabin-Karp. Parent: KMP — full derivation.


Step 1 — What are we even looking at? Prefix, suffix, border

WHAT. Take a string , say ababa. A prefix is any chunk you get by reading from the left and stopping. A suffix is any chunk you get by reading up to the right end. A border is a chunk that is both — the same characters appear at the start and at the end.

WHY. Everything in KMP is one question: "After I've matched some characters, which of them at the front are secretly a copy of the ones at the back?" That copy is a border. So we must be able to see borders before anything else.

PICTURE. Below, the blue bar is a prefix growing from the left; the orange bar is a suffix growing from the right. Where a blue chunk and an orange chunk hold the exact same letters, we have a border (green). aba is a border of ababa: front aba = back aba.

Figure — KMP algorithm — failure function, O(n+m) — full derivation

Step 2 — Why a border is exactly what a mismatch needs

WHAT. Line the pattern up under the text . Recall from the definitions above that = the current length of the matched prefix. Suppose the first characters of matched, and then character disagrees with the text character .

WHY. We refuse to move the text pointer backwards. But the pattern must slide right to try again. How far? Look at what we already know: the last text characters we scanned are equal to the first pattern characters (that's what "matched" means). If the front of the pattern reappears at the back of that matched run — a border! — then sliding the pattern so its front lands on that border needs no re-reading: those characters already agree.

PICTURE. The green matched run is the same on top (text ) and bottom (pattern ). The longest border of that run (darker green) is the only slide that keeps us honest: shorter would re-check nothing new, longer would skip a possible match.

Figure — KMP algorithm — failure function, O(n+m) — full derivation

Step 3 — Reading π off ababaca by eye

WHAT. Compute for the string ababaca, position by position, purely by spotting the longest green border.

WHY. Before we automate it, you must trust that is just "measure the longest border here." Automation is only a fast way to do exactly this.

PICTURE. Each row grows the string by one letter. The green bar shows the longest proper border found; its length is .

Figure — KMP algorithm — failure function, O(n+m) — full derivation
prefix longest proper border
0 a (none) 0
1 ab (none) 0
2 aba a 1
3 abab ab 2
4 ababa aba 3
5 ababac (none, ends in c) 0
6 ababaca a 1

Each symbol: = how far right we've read; = the green length. At the string ends in c, and no front chunk of ababac ends in c, so the border vanishes to .


Step 4 — Building π: the "extend" move (Case 1)

WHAT. Suppose we already know the answer one step back: let , the best border length for the previous prefix. Now we add character . If equals , the border grows by one on both ends at once.

WHY. A border of length means "first chars = last chars." The character sitting just after that front copy is . The new character we just appended is . If they're equal, we can stretch both the front copy and the back copy by one letter — a longer border. So .

PICTURE. The green border of length is flanked by two equal letters (the one at index in front, the one at index at the back). Matching them zips the border up to length .

Figure — KMP algorithm — failure function, O(n+m) — full derivation

Step 5 — Building π: the "fall back" move (Case 2)

WHAT. Now and . We cannot extend. Instead of scanning from scratch, we jump and try again.

WHY. We wanted a border of length to extend, but the follow-up letter didn't match. The next candidate must be a shorter border of the same matched run — and the longest border shorter than is precisely the longest border of that border, which is stored at . So we walk down a chain of nested borders, each one the border of the last, until either the letters match (extend) or we hit .

PICTURE. A ladder of shrinking green borders: length , then , then , … Each rung is the border of the rung above. We stop at the first rung whose next character matches .

Figure — KMP algorithm — failure function, O(n+m) — full derivation

Step 6 — Degenerate case: bottomed out (Case 3)

WHAT. The fall-back chain can reach . If still doesn't match , there is no border at all — set and move on.

WHY. means "no matched front left to reuse." At that point the only question is whether the new letter equals the very first letter : if yes, ; if no, . There is nothing below zero — this is the floor that guarantees the loop always stops.

PICTURE. The green ladder has hit the ground. Two outcomes: new letter equals (tiny green border of length 1) or it doesn't (bare zero).

Figure — KMP algorithm — failure function, O(n+m) — full derivation

Step 7 — Why the whole thing is O(n+m): watch j, not i

WHAT. Run the matcher across the text (length ). Track a single quantity: the value of (how many pattern characters are currently matched).

WHY. Two facts about :

  • The text loop runs times, and each pass raises by at most 1 (j += 1). So over the whole run, goes up by at most .
  • Every fall-back step (j = π[j-1]) lowers by at least 1 (a proper border is strictly shorter). Since always, it can't fall more than it rose. So total fall-back steps total rises . The inner while runs times across the entire run, not per character.

PICTURE. A staircase: climbs one step at a time (blue up-arrows) and occasionally plunges (orange down-arrows). The total drop can never exceed the total climb — that's the amortized bound. (See Amortized Analysis for this "potential" style of argument.)

Figure — KMP algorithm — failure function, O(n+m) — full derivation
Recall Where do the two terms

and come from? ::: the matching loop over the text : text pointer moves forward times, total fall-backs . ::: the identical staircase argument while building over the pattern of length .


Step 8 — The machine in motion: pattern abab, text ababaababab

WHAT. Watch the derived rules run on a real search. Here from Steps 1–6 becomes the pattern abab, so for is . We slide under the text ; on a full match ( reaches ) we report the start index and set to catch overlapping matches.

WHY. This is where Steps 4–6 pay off: at text index a mismatch fires the fall-back ladder match, reusing the single a instead of restarting — exactly the border logic, live.

PICTURE. The pattern (blue) sitting under the text (gray). Green cells = matched, red cell = the mismatch that triggers a slide. The reported overlapping matches start at text indices , , and .

Figure — KMP algorithm — failure function, O(n+m) — full derivation
before what happens after
3 b 3 match → , report start 0, 2
5 a 3 mismatch: , then , then a=a 1
7 b 3 match → , report start 4, 2
9 b 3 match → , report start 6, 2

Matches start at text indices — the last two overlap, which is only possible because we set (not ) after a hit.


The one-picture summary

Everything above collapses into one diagram: a border of the matched run is the only safe slide, and pre-measures it so the text pointer never retreats.

Figure — KMP algorithm — failure function, O(n+m) — full derivation

In the flowchart below, remember: = text, = pattern, = text index (forward only), = matched-prefix length, = pattern length, and = the failure function we built in Steps 1–6.

equal

not equal and j greater than 0

not equal and j is zero

full match

Compare text char at i with pattern char at j

j goes up by one

j becomes pi of j minus one

move i forward

j reaches m

report start then j becomes pi of m minus one

Recall Feynman retelling of the whole walkthrough

You're checking whether a small word (the pattern ) hides inside a big book (the text ), reading the book left to right and never going back. You match letters until one slips. A dumb reader would restart the word from its first letter at the next book position. The smart reader notices something: "the last few letters I just matched are the same letters my word starts with." That repeated chunk — same at the front and the back — is a border. Instead of restarting, the reader slides the word so its front lands exactly on that border; those letters already agree, so no re-reading. The cheat-sheet says, for every spot in the word, how long that reusable border is. Building the cheat-sheet is the same trick applied to the word against itself: extend the border when the next letter matches, and drop to the border-of-the-border when it doesn't. Because the "matched count" can only fall as much as it climbed, and it climbs at most once per book letter, the whole search finishes in time proportional to book + word — that's .