Lukas' Notes

Definition

Knuth–Morris–Pratt Algorithm (String Matching)

The Knuth–Morris–Pratt algorithm solves the string matching problem by using repeated structure in the pattern to retain successful comparisons after a mismatch. Given text of length and pattern of length , it returns all zero-based occurrence positions:

It first computes the pattern’s prefix function, then scans the text without moving the text index backwards. The running time is , including empty inputs, with auxiliary space excluding the output. An empty pattern occurs at all text boundaries.

Discovering the Algorithm

A Mismatch Does Not Erase the Matches

Suppose we want to find this pattern. The numbers above the characters are zero-based indices.

Here is the text we will search. For now, let us simply place the pattern at its beginning and compare from left to right.

The first five comparisons succeed. Then the text has b where the pattern expects c. This occurrence has failed—but we have learned that the first five text characters are ababa.

The naïve approach moves the pattern one position right and starts again. Start fails immediately. Start is more interesting: its first three comparisons succeed, spelling aba.

Those three text characters were already checked. Can we know that they match the new alignment without reading them again?

Set the text aside for a moment. The part of the pattern that matched was ababa. Its ending aba is exactly its beginning aba. Orange marks these equal parts in the diagram below.

Now combine two facts. The successful comparisons told us that the text ends with the matched ababa. The pattern tells us that this ending aba equals its beginning. Therefore those text characters already match the beginning of the shifted pattern.

The reusable part is a border: both a prefix and a suffix of the matched string. We want the longest proper border. Keeping the whole string would leave the pattern at the alignment that just failed.

Turn the Repeated Structure into a Jump

We will need this information after different numbers of successful comparisons. Rather than discover the border each time, store its length for every pattern prefix. This table is the prefix function: gives the longest proper border length of , with zero for the empty border.

Suppose characters have matched. Their indices are : a length of five ends at index four. That is why we read .

Here the table takes the ending index and returns the retained length . Reading instead would ask about ababac, including the c that failed to match.

Where should the pattern move? The retained aba occupies the last three positions of the five-character match. Its beginning is two positions after the old start. In general, discard positions and retain :

Let be the next text index. Before the move, characters extend backwards from ; afterwards, only do. Thus

Bring the text back. Move the pattern by , retain the orange aba, and retry the pending text character against . Nothing requires us to move backwards in the text.

This comparison succeeds: both characters are b. We now have four matched characters and can advance the text index. The move has avoided both the impossible start and the three repeated comparisons at start .

But what if the retry had failed? We would need a shorter suffix that is still a pattern prefix. Such candidates are precisely the nested borders of the part we retained. The same lookup works again: replace the current length by .

At fixed text index , the lengths give starts . Starts and cannot work: baba and ba do not equal the corresponding pattern prefixes. Each arrow is taken only after another mismatch; a successful comparison stops the fallback.

At length zero, try . If even that fails, there is nothing left to retain: advance the text index. So the search needs just a text position and a matched length —the pattern’s start is implicit in .

There is one more place where we should keep a border: after a complete match. If characters have matched through text index , report start . Then retain characters, since the next occurrence may share the end of this one.

Build the Table with the Same Idea

We have derived the search assuming the table is available. Can we also build that table without repeatedly comparing the same pattern characters?

Suppose the table is known through index . Its last value gives the longest border of the prefix seen so far. To extend that border by one character, compare with the new character .

If they agree, store . If they disagree, try the next shorter border, , against the same . This is the search mechanism again, but entirely inside the pattern.

In the diagram, the new c cannot extend any candidate, so . At the next index, a agrees with , giving . Start with and proceed left to right to obtain the entire table.

We now have both phases: first learn where each pattern prefix repeats at its own end; then use that information to retain matches while scanning the text. The two loops below use the same fallback rule.

Pseudocode

Slices use inclusive endpoints; an empty range or slice contains no elements. and short-circuits. The array access pi[q] denotes .

compute_pi(P):
    m := |P|
    pi := array of m zeros
    k := 0
    for q := 1 to m - 1:
        while k > 0 and P[k] != P[q]:
            k := pi[k - 1]
        if P[k] == P[q]:
            k := k + 1
        pi[q] := k
    return pi
 
kmp_search(T, P):
    n := |T|
    m := |P|
    if m == 0:
        return [0, ..., n]
 
    pi := compute_pi(P)
    occurrences := []
    L := 0
    for i := 0 to n - 1:
        while L > 0 and P[L] != T[i]:
            L := pi[L - 1]
        if P[L] == T[i]:
            L := L + 1
        if L == m:
            append i - m + 1 to occurrences
            L := pi[m - 1]
    return occurrences

There is no explicit shift variable. A fallback changes without consuming ; the outer loop consumes that character only after the retries finish.

Correctness

Fallback Enumerates Exactly the Shorter Candidates

Border candidates

Suppose is a suffix of the consumed text. For ,

Consequently, repeated lookups enumerate all shorter suffix matches in decreasing length.

Proof

The last characters of the consumed text lie inside its suffix . They are therefore exactly the last characters of .

Matching them with is equivalent to matching the first and last characters of . This is the border condition.

The longest proper border is given by . By nesting and transitivity, every shorter border is a border of that border, and conversely. Repeating the lookup skips no border and strictly decreases the length until zero.

The Prefix Table Is Correct

Correct prefix values

compute_pi(P) returns the prefix function of .

Proof

Base: a one-character prefix has only the empty proper border, so . For empty , the returned array is empty.

Induction: before processing , earlier table entries are correct and .

A non-empty proper border of of length must satisfy

Thus its first characters form a proper border of , and its last character extends that border. Conversely, any such extension gives a proper border of .

The fallback lemma enumerates all candidate lengths from largest to smallest. The first successful comparison gives the longest extension; if none succeeds, zero is correct. Hence the stored is correct, completing the induction.

The Search Reports Exactly All Occurrences

For , define the candidate lengths before processing text index :

The invariant at the start of each outer iteration is . It deliberately excludes length : complete matches ending before have already been reported.

Exact occurrence positions

kmp_search(T, P) returns all and only the positions where occurs in , in increasing order, including overlapping occurrences.

Proof

Initialisation: before , the consumed text is empty and . Thus satisfies the invariant.

Extension: a non-empty prefix match of length ending at exists exactly when

Removing its last character proves necessity; appending the matching character proves sufficiency.

Fallback: starting at , the lemma enumerates all candidates in decreasing order. A mismatch rejects only the current candidate. The first successful comparison therefore gives the greatest length ending at ; if all fail, that length is zero.

Reporting: if , then , so the reported start is valid. Conversely, any occurrence ending at gives and is reported. No endpoint is omitted because the outer loop visits every text character.

Restoring the invariant: if , retain . If , the longest shorter suffix match is the longest proper border of , so set . Either way, .

Termination: each fallback strictly decreases a positive length; each outer iteration consumes one text character. All iterations finish, establishing the result. If , the explicit branch returns exactly the boundaries.

Complexity

A text character may be compared more than once during fallback. Linear time follows from counting changes of the matched length, not from claiming one comparison per character.

During search, increases by at most one per text character. Each fallback decreases it by at least one; resets after full matches also decrease it. Since starts at zero and stays non-negative,

The remaining work is constant per text character and occurrence. The same argument with gives linear prefix-table construction.

PhaseTimeAuxiliary space
Build the prefix table
Scan the text beyond the table
Total excluding output

For occurrences, the output list uses space. Character equality is assumed to take constant time. The algorithm handles exact contiguous matching; wildcard or approximate matching requires different rules.

Examples

A complete scan with one fallback

For and , the prepared table is .

Text index beforeAction after, including reset
0a0Match 1
1b1Match 2
2a2Match 3
3b3Match 4
4a4Match 5
5b5Fail at ; retain ; match 4
6a4Match 5
7c5Match 6
8a6Reach ; report ; retain 1

The returned list is .

Overlapping matches

For and , the returned list is . After the first match, retaining preserves the shared a at text position .

Empty and impossible patterns

For and , return . For non-empty and empty , or for , return .