3.8.2 · D3String Algorithms

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

3,165 words14 min readBack to topic

The scenario matrix

Every string the failure function can face falls into one of these case classes. The examples below hit every cell.

# Case class What makes it special Example
A Generic pattern (mixed borders) rises and falls, normal fallback chain Ex 1
B All-identical characters aaaa climbs — maximal borders Ex 2
C No border at all abcd stays everywhere — worst reuse Ex 3
D Nested / chained borders one mismatch falls back through several slots Ex 4
E Degenerate inputs — length-1 pattern, empty pattern, empty text ; and edges Ex 5
F Overlapping matches full match, then continue via not Ex 6
G No match exists text pointer marches, never reaches Ex 7
H Amortized limit (adversarial) the -killer input, proving linearity Ex 8
I Real-world word problem applied framing Ex 9
J Exam twist — smallest period from using for a different question Ex 10

Example 1 — Generic pattern (Cell A)

Forecast: Guess: does reach before dropping? Where does it drop to ?

  1. : by definition (single char, no proper prefix). Why this step? Every build starts here — it is the base case of the recurrence.
  2. (b): j=pi[0]=0; S[1]=b, S[0]=a, mismatch, j=0 already → (Case 3). Why? No prefix of ab is also a suffix.
  3. (c): j=0; c≠a (Case 3). Why? With j=0 we compare the new char c to the very first char a; they differ and is already , so no prefix of abc is a suffix — border length stays .
  4. (a): j=pi[2]=0; S[3]=a, S[0]=a match → j=1, (Case 1). Why? The prefix a now reappears as a suffix of abca.
  5. (b): j=pi[3]=1; S[4]=b, S[1]=b match → j=2, (Case 1). Why? Border ab grew from border a by one matching char.
  6. (c): j=2; S[5]=c, S[2]=c match → j=3, (Case 1). Why? The border was ab (length 2); the new char c equals S[2]=c, so the border extends by one to abc (length 3) — Case 1 firing a third time in a row.
  7. (d): j=3; S[6]=d, S[3]=a mismatch → j=pi[2]=0 (Case 2); d≠a (Case 3). Why? d breaks every border; we fall to the bottom.

Answer: .

Verify: abcabc has longest proper prefix=suffix abc (length 3) ✓; adding d kills all borders ✓.


Example 2 — All-identical characters (Cell B)

Forecast: Will be or something smaller?

  1. : .
  2. : j=0; a==aj=1, (Case 1). Why? Prefix a = suffix a of aa. Proper (length 1 < 2). ✓
  3. : j=pi[1]=1; S[2]=a, S[1]=a match → j=2, (Case 1). Why? The previous border was a (length 1); the new char equals S[1]=a, so the border extends to aa (length 2).
  4. : j=pi[2]=2; S[3]=a, S[2]=a match → j=3, (Case 1). Why? Border aa (length 2) extends by the matching char S[2]=a to aaa (length 3); still proper because .

Answer: .

Verify: For aaaa, the longest proper prefix that is a suffix is aaa (length ) — never , because proper forbids the whole string. This is exactly why we insist on proper in the definition: allowing the whole string would give everywhere and the fallback j=pi[j-1] would never shrink , causing an infinite loop.


Example 3 — No border anywhere (Cell C)

Forecast: What does look like when nothing ever repeats?

  1. .
  2. : j=pi[0]=0; S[1]=b, S[0]=a, b≠a (Case 3). Why? j is already and the new char b differs from the first char a, so no prefix of ab is a suffix — border stays .
  3. : j=pi[1]=0; S[2]=c, S[0]=a, c≠a (Case 3). Why? Same situation: c differs from the first char a, so abc has no prefix=suffix.
  4. : j=pi[2]=0; S[3]=d, S[0]=a, d≠a (Case 3). Why? d differs from a; abcd has no repeated character, so no border can exist.

Answer: .

Verify: No character repeats, so no prefix can equal any suffix — is all zeros. On a mismatch, matching such a pattern behaves exactly like naive (nothing to reuse), yet still because never rewinds. See Borders and Periods of Strings for the border-free case.


Example 4 — Nested border chain (Cell D)

Forecast: At the last char, how many times does the while loop fire?

  1. . Why? Base case — a single char a has no proper prefix.
  2. (a): j=pi[0]=0; a==a (Case 1). Why? The new char a equals the first char S[0]=a, so aa has border a (length 1).
  3. (b): j=pi[1]=1; S[2]=b, S[1]=a mismatch → j=pi[0]=0 (Case 2); b≠a (Case 3). Why? The border a cannot extend (b≠a), so we fall back to ; still no match, so border length is .
  4. (a): j=pi[2]=0; a==a (Case 1). Why? With j=0, the new char a equals S[0]=a, restarting a border of length 1 for aaba.
  5. (a): j=pi[3]=1; S[4]=a, S[1]=a match → (Case 1). Why? Border a (length 1) extends by the matching char S[1]=a to aa (length 2).
  6. (a): j=pi[4]=2; S[5]=a, S[2]=b mismatch → j=pi[1]=1 (Case 2); S[5]=a, S[1]=a match → (Case 1). Why the fallback? Border of length 2 couldn't extend (b≠a), so we asked "what is the longest border inside that border?" — — and tried again. This is the border chain walking down (Case 2).
  7. (b): j=pi[5]=2; S[6]=b, S[2]=b match → (Case 1). Why? Border aa (length 2) extends by the matching char S[2]=b to aab (length 3).

Answer: .

Verify: aabaaab — longest proper prefix=suffix is aab (length 3): prefix aab = last three chars aab ✓. Step 6 fired the while once (Case 2) then matched (Case 1) — the chained fallback in action.


Example 5 — Degenerate inputs (Cell E)

Forecast: Can a single character have a border? How many matches does an empty pattern have?

  1. Length-1 pattern x: by definition — a single char has no proper prefix (the only prefix is the whole thing, which is forbidden). Why? Proper prefix must be strictly shorter; nothing shorter than length 1 except the empty string, which gives border length 0.
  2. Empty pattern (): the build loop for i=1..m-1 never runs; is the empty array. In matching, j==m is j==0 — a match is reported at every position (the empty string occurs everywhere). Guard this in code with an explicit if m==0 to avoid reading pi[m-1]=pi[-1]. Why care? Contest judges love empty-input edge cases; forgetting the guard gives out-of-range reads.
  3. Empty text (): the match loop for i=0..n-1 never runs, so stays . Result: zero matches for any non-empty pattern — correct, since nothing can occur in nothing. (If too, conventions differ; most define the empty pattern to match once, at position 0.) Why mention it? It is the mirror image of the empty pattern — the degenerate case on the text side, and it must not crash: an empty loop simply produces an empty match list.

Answer: ; empty pattern → matches everywhere; empty text → no matches (for non-empty ).

Verify: For x, longest proper prefix=suffix length is ✓. Searching any pattern in an empty text yields ✓.


Example 6 — Overlapping matches (Cell F)

Forecast: How many matches — 2 or 3? (Naive-reset thinking says 2.)

First, and .

before action after
0 a 0 match → j=1 1
1 a 1 match → j=2=m, report , j=pi[1]=1 1
2 a 1 match → j=2=m, report , j=1 1
3 a 1 match → j=2=m, report , j=1 1

Why this step (the j=pi[m-1] on match)? After reporting, we keep the overlap a (length ) instead of throwing it away, so the next a completes the next overlapping match. Resetting j=0 would drop the overlap and miss matches.

Answer: matches at indices three overlapping aa.

Verify: aaaa contains aa starting at 0,1,2 ✓. Resetting j=0 would have missed indices 1 and 2 — this is why a full match continues via , not .


Example 7 — No match exists (Cell G)

Forecast: Does ever reach ?

, .

before action after
0 a 0 a==aj=1 1
1 b 1 b==bj=2 2
2 a 2 P[2]=x≠a, j=pi[1]=0; a==aj=1 1
3 b 1 b==bj=2 2
4 a 2 x≠a, j=0; a==aj=1 1
5 b 1 b==bj=2 2

Why does still march forward on every mismatch? Because the reused prefix P[0..π[j-1]-1] is already known equal to the text — re-reading it is wasted work. tops out at , never .

Answer: no matches; never reaches .

Verify: abx does not occur in ababab ✓ (no x). Total text reads = 6, one per char — linear even with zero matches.


Example 8 — Adversarial amortized limit (Cell H)

Forecast: How many inner-while steps fire in total across the whole run?

, , .

The interesting positions: while the text feeds a, climbs . Let us count the amortized budget instead of every row:

  1. Total j increases (at most one +1 per text char). Why? The if T[i]==P[j] line runs once per .
  2. Total while decreases total increases , since always and each while step drops by . Why? behaves like a bank account: deposits , so withdrawals .
  3. Therefore inner steps across the entire scan are , not .

Running it, the single match is aaab: the b is at index 7, and aaab ends there, starting at .

Answer: one match at index ; total inner-while steps → runtime , not . See Amortized Analysis.

Verify: aaaaaaab[4:8] aaab ✓; and no earlier start works because a match needs the b.


Example 9 — Real-world word problem (Cell I)

Forecast: Overlaps again — 1 hit or 2?

, .

before action after
0 A 0 match → j=1 1
1 T 1 match → j=2 2
2 A 2 match → j=3=m, report , j=pi[2]=1 1
3 T 1 match → j=2 2
4 A 2 match → j=3=m, report , j=1 1
5 T 1 match → j=2 2

Why report 0 and 2? The overlap A (length ) is preserved after each hit, so the next TA completes the following motif. A gene aligner must catch overlaps — biology does not care that they share a base.

Answer: motif ATA starts at indices .

Verify: ATATAT[0:3]=ATA ✓, ATATAT[2:5]=ATA ✓, no start at index 4 (only AT remains).


Example 10 — Exam twist: smallest period from (Cell J)

Forecast: Is the answer 3? Or 9 (no repetition)?

  1. Build the last value. By the same climb as Ex 1, abcabcabc has (longest proper prefix=suffix is abcabc). Why ? The first 6 chars abcabc equal the last 6 abcabc.
  2. Apply the period formula. . Why does this work? A border of length means the string, shifted right by , still lines up with itself on the overlapping region. A shift that leaves the string aligned with itself is precisely a period. So the smallest such shift, , is the smallest period candidate. See Borders and Periods of Strings.
  3. Check the divisibility condition . Here and , and , so . This is what upgrades from "a shift that aligns" to "the string is exactly full copies of the block abc." If did not divide (say the string were abcabca, , , , but ), the string would be only partially periodic — the last block is a fragment — and the smallest full period would be itself. Why check it at all? Because the question asks for a period that tiles completely; without the block does not tile, so the answer must fall back to .

Answer: smallest period (abc repeated three times), and since this is a genuine full period.

Verify: , ✓; abc×3 = abcabcabc ✓. Counter-check: abcabca gives but , so it is not fully periodic ✓.


Recall Which cell was hardest?

The nested chain (Ex 4) and the overlap trap (Ex 6). If you can do those two cold, the rest are routine. Which example proves linearity? ::: Example 8 — the adversarial aaaaaaab amortized count. Which formula did the exam twist use? ::: smallest full period when it divides .

Active Recall

What is for abcabcd?
How many occurrences of aa in aaaa, and at which indices?
Three, at indices 0, 1, 2 (overlapping).
Smallest period of abcabcabc via the failure function?
, and so it is a full period.