โ† Wiki Index / Computation & Algorithms / Knuth-Morris-Pratt Algorithm
๐Ÿ’ป Computation & Algorithms

Knuth-Morris-Pratt Algorithm

The Knuth-Morris-Pratt (KMP) algorithm is a deterministic linear-time string-matching algorithm that locates all occurrences of a pattern of length m within a text of length n in O(n + m) worst-case time using a precomputed prefix border table.

The Knuth-Morris-Pratt algorithm (KMP algorithm) is a deterministic linear-time substring search method in computation and algorithms that locates all occurrences of a pattern string P of length m within a text string T of length n in O(n + m) worst-case time without ever backtracking the text pointer. Conceived in 1970 by Donald E. Knuth, James H. Morris, Jr., and Vaughan R. Prattโ€”and published jointly in the SIAM Journal on Computing in 1977โ€”the algorithm preprocesses the pattern in ฮ˜(m) time and space to construct an auxiliary prefix function (also called the failure function or longest proper border array), denoted ฯ€[1..m]. Whenever a character mismatch occurs after q matched symbols, ฯ€[q] tells the matcher the longest prefix of P that is simultaneously a proper suffix of the already-matched substring P[1..q], enabling the pattern to shift forward to the next viable alignment while preserving all information gained from previous character comparisons.

Prior to KMP, the standard brute-force sliding-window matcher tested each of the n โˆ’ m + 1 candidate starting positions independently, resetting the text index backward after every mismatch and incurring ฮ˜(nm) worst-case time on repetitive inputs such as searching for P = "AAAAB" inside T = "AAAAAAAAAAAAAAAAB". Inspired by Stephen Cook's 1970 theorem that any two-way deterministic pushdown automaton can be simulated on a random-access machine in linear time, Knuth and Pratt independently derived the general automaton simplification while Morris implemented the equivalent prefix-shift table inside a production text editor for the CDC 6400 operating system at the University of California, Berkeley.

Mathematical Formulation: Borders and the Prefix Function

Proper Borders of a Substring

Let ฮฃ be a finite or countably infinite alphabet, let T[1..n] โˆˆ ฮฃn be the text string, and let P[1..m] โˆˆ ฮฃm be the query pattern (with m โ‰ค n). For any integer q โˆˆ {1, 2, ..., m}, let Pq = P[1..q] denote the prefix of P of length q.

A substring w is called a border of Pq if w is both a prefix of Pq and a suffix of Pq. A border w is a proper border if 0 โ‰ค |w| < q (meaning w does not span the entire string Pq). Suppose that at a given stage of the search, the first q characters of the pattern match the text ending at text position i:

P[1..q] = T[i โˆ’ q + 1 .. i]

while the next character mismatches (P[q + 1] โ‰  T[i + 1], or q = m after a complete match). Any smaller shift of the pattern that aligns a shorter prefix P[1..k] (with 0 โ‰ค k < q) against the text suffix T[i โˆ’ k + 1 .. i] can succeed only if P[1..k] equals the suffix of P[1..q] of length kโ€”that is, only if P[1..k] is a proper border of Pq.

Definition of the Prefix Table ฯ€[q]

The KMP prefix function ฯ€ : {1, 2, ..., m} โ†’ {0, 1, ..., m โˆ’ 1} assigns to each prefix length q the length of the longest proper border of Pq:

ฯ€[q] = max { k : 0 โ‰ค k < q and P[1..k] is a suffix of P[1..q] }

By definition, ฯ€[1] = 0 because the only proper prefix of a single-character string is the empty string ฮต of length 0. Furthermore, iterating the prefix function generates every proper border of Pq in strictly decreasing order of length:

  • Border sequence: ฯ€[q] > ฯ€[ฯ€[q]] > ฯ€(3)[q] > ยทยทยท > ฯ€(r)[q] = 0

This border transitivity lemma holds because any proper border of length j < ฯ€[q] of Pq sits inside the suffix of Pq of length ฯ€[q], which is identical to P[1..ฯ€[q]]; hence j must also be a proper border of Pฯ€[q].

Worked Example of ฯ€[q] on a Repetitive Genomic Motif

Consider the 10-nucleotide pattern P[1..10] = "ACACAGTACA" (m = 10). The table below lists each prefix Pq, its longest proper border, and the resulting shift value q โˆ’ ฯ€[q]:

Index qCharacter P[q]Prefix P[1..q]Longest Proper BorderPrefix Function ฯ€[q]Strong Automaton Table ฯ€โ€ฒ[q]
1AAฮต (empty)00
2CACฮต00
3AACAA10
4CACACAC20
5AACACAACA33
6GACACAGฮต00
7TACACAGTฮต00
8AACACAGTAA10
9CACACAGTACAC20
10AACACAGTACAACA31

If a mismatch occurs at q = 5 (after matching "ACACA" against the text and failing on P[6] = 'G'), the algorithm looks up ฯ€[5] = 3, shifting the pattern forward by 5 โˆ’ 3 = 2 positions so that P[1..3] = "ACA" remains aligned with the last three matched text symbols without re-reading them, and immediately tests P[4] = 'C' against the current text character T[i + 1].

Algorithm Execution and Amortized Complexity Analysis

Two-Phase Linear-Time Procedure

The Knuth-Morris-Pratt algorithm uses two nearly identical routines: Compute-Prefix-Function(P) (which matches P[2..m] against P[1..m โˆ’ 1] using dynamic programming along the border chain) and KMP-Matcher(T, P) (which scans T[1..n]).

In Compute-Prefix-Function(P):

  1. Initialize ฯ€[1] โ† 0 and border length k โ† 0.
  2. For q = 2 to m:
    • While k > 0 and P[k + 1] โ‰  P[q], fall back along the border chain: k โ† ฯ€[k].
    • If P[k + 1] = P[q], increment the matched border length: k โ† k + 1.
    • Record ฯ€[q] โ† k.

In KMP-Matcher(T, P):

  1. Compute ฯ€[1..m] and initialize matched prefix length q โ† 0.
  2. For text index i = 1 to n:
    • While q > 0 and P[q + 1] โ‰  T[i], fall back: q โ† ฯ€[q].
    • If P[q + 1] = T[i], advance the pattern state: q โ† q + 1.
    • If q = m, report a full pattern occurrence at starting index i โˆ’ m + 1 and set q โ† ฯ€[m] to search for overlapping matches.

Amortized Potential Proof of ฮ˜(n + m) Time

At first glance, the inner while loop (q โ† ฯ€[q]) inside the outer for loop (i = 1 to n) might appear to require O(m) steps per text character, which would yield O(nm). However, an amortized potential argument (a cornerstone of computational complexity theory) proves that the inner loop executes at most n times in total across the entire scan of T:

  • Define the non-negative potential function ฮฆ(i) = q, the length of the currently matched pattern prefix after processing T[i], initialized at ฮฆ(0) = 0.
  • During iteration i of the outer for loop, the conditional match step (P[q + 1] = T[i]) can increment q by at most 1. Across all n iterations of the outer loop, q can therefore increase by at most n units in total.
  • Conversely, every execution of the body of the inner while loop replaces q with ฯ€[q]. Because ฯ€[q] โ‰ค q โˆ’ 1 for all q โ‰ฅ 1, each inner-loop iteration decreases ฮฆ = q by at least 1.
  • Because ฮฆ starts at 0, never becomes negative (ฮฆ(i) โ‰ฅ 0), and increases at most n times by 1, the inner while loop body can execute at most n times across the entire run.

Each character comparison P[q + 1] โ‰Ÿ T[i] either succeeds (advancing i in the outer loop, at most n times) or fails (triggering a border fallback or leaving q = 0). More tightly, because the first text character T[1] cannot trigger a fallback, the total number of character comparisons during the text scan is bounded by 2n โˆ’ 1 (and during preprocessing by 2m โˆ’ 3).

Strong Prefix Table (ฯ€โ€ฒ) and Fibonacci String Delay Bound

In the basic prefix table ฯ€[q], a mismatch on P[q + 1] โ‰  T[i] may shift the pattern to state k = ฯ€[q] where the next pattern character P[k + 1] is identical to the character P[q + 1] that just failed against T[i]. For example, in our table for "ACACAGTACA", at q = 4 (P[5] = 'A'), falling back to k = ฯ€[4] = 2 leads to P[3] = 'A', which is guaranteed to fail again against T[i].

Knuth, Morris, and Pratt eliminated these redundant comparisons by defining the strong failure table ฯ€โ€ฒ[q]:

  • ฯ€โ€ฒ[q] = ฯ€[q] if P[ฯ€[q] + 1] โ‰  P[q + 1]
  • ฯ€โ€ฒ[q] = ฯ€โ€ฒ[ฯ€[q]] if P[ฯ€[q] + 1] = P[q + 1]

With ฯ€โ€ฒ[q], the worst-case number of consecutive fallback steps on a single text character T[i] occurs when P is a Fibonacci word (built by Fk = Fkโˆ’1Fkโˆ’2, such as "ABAABABAABAAB"), where the maximum delay on a single text symbol is bounded by 1 + logฯ† m comparisons, where ฯ† = (1 + โˆš5) / 2 โ‰ˆ 1.618 is the golden ratio.

Comparison with Other String-Matching Paradigms and Applications

AlgorithmPreprocessing Time & SpaceMatching Worst-Case TimeMatching Average TimeStreaming (Zero Backtracking)
Naive Brute-ForceO(1)ฮ˜(nm)O(n)No (backs up text pointer i)
Knuth-Morris-Pratt (KMP)ฮ˜(m) time, ฮ˜(m) spaceฮ˜(n) (โ‰ค 2n โˆ’ 1 comparisons)ฮ˜(n)Yes (processes T[i] online)
Boyer-Mooreฮ˜(m + |ฮฃ|)ฮ˜(n) (with Galil's rule)O(n / m) sublinearNo (requires random access in T)
Rabin-Karp (Rolling Hash)ฮ˜(m) time, O(1) spaceฮ˜(nm) (Las Vegas)O(n + m)Yes (sliding window of size m)
Aho-Corasick (k patterns)ฮ˜(โˆ‘ mj) trie + failure linksฮ˜(n + z) (z = matches)ฮ˜(n + z)Yes (single-pass multi-pattern)

Because KMP never backtracks in the text stream T and requires only O(m) working memory independent of alphabet size |ฮฃ|, it is the algorithm of choice when reading unbuffered network sockets, UNIX pipes, or magnetic tape streams, and when the alphabet |ฮฃ| is small (such as binary bitstreams or the 4-letter DNA alphabet {A, C, G, T}) where right-to-left skipping algorithms gain fewer long shifts. In bioinformatics, KMP and its multi-pattern generalizationโ€”Alfred V. Aho and Margaret J. Corasick's 1975 Aho-Corasick trie automatonโ€”are used for rapid scanning of restriction endonuclease cleavage sites, CRISPR protospacer adjacent motifs (PAMs), adapter trimming in raw sequencing reads, and seed filtering prior to banded sequence alignment.

Frequently Asked Questions

Why does the Knuth-Morris-Pratt algorithm never move the text pointer backward?

At every step i, the matched prefix state q records that the last q characters read from the text (T[i โˆ’ q + 1 .. i]) are identical to the pattern prefix P[1..q]. Because those q text characters are already known to equal P[1..q], any valid shift of the pattern within that window depends only on self-overlaps (proper borders) inside P[1..q], which are precomputed in ฯ€[q] before reading the text.

When is Boyer-Moore preferred over the Knuth-Morris-Pratt algorithm?

The Knuth-Morris-Pratt algorithm inspects every text character at least once, taking ฮ˜(n) time in both the best and worst cases. When the text resides in random-access memory and the alphabet |ฮฃ| is moderately large (such as ASCII text or 20-letter protein sequences) with pattern length m โ‰ฅ 5, the Boyer-Moore algorithm scans the pattern from right to left and skips across non-matching text segments in O(n / m) sublinear average time.

How does KMP relate to the Aho-Corasick multi-pattern algorithm?

The Aho-Corasick algorithm (1975) is the direct generalization of KMP from a single pattern string to a finite dictionary of k patterns {P1, ..., Pk}. It organizes all patterns into a prefix tree (trie) and equips every trie node u with a KMP-style failure link pointing to the node representing the longest proper suffix of u's string that is also a prefix of some pattern in the dictionary.

References

  1. Knuth, D. E., Morris, J. H., Jr., & Pratt, V. R. (1977). Fast pattern matching in strings. SIAM Journal on Computing, 6(2), 323โ€“350. https://doi.org/10.1137/0206024
  2. Aho, A. V., & Corasick, M. J. (1975). Efficient string matching: An aid to bibliographic search. Communications of the ACM, 18(6), 333โ€“340. https://doi.org/10.1145/360825.360855
  3. Cook, S. A. (1971). Linear time simulation of deterministic two-way pushdown automata. In Proceedings of the IFIP Congress 1971, Vol. 1 (pp. 75โ€“80). North-Holland. https://doi.org/10.1145/800157.805047
  4. Crochemore, M., & Rytter, W. (2002). Jewels of Stringology: Text Algorithms. World Scientific Publishing. https://doi.org/10.1142/4838
๐Ÿงฌ Support Open Science
Explore interactive science games on LowKey Games or browse scientific apparel & accessories in the CRV Science Shop.
๐Ÿ’ก Spot an inaccuracy or have primary literature to expand this article?
Submit a citation or revision note using your shared CRV Science / LowKey Games profile ID.
Suggest Revision