Strings · String Fundamentals

KMP Failure Function Application

Building the LPS (longest-proper-prefix-that-is-also-a-suffix) array for a string in one linear pass, using the array's own already-computed values to avoid re-scanning characters that a mismatch has already ruled out.

  • 6 min read
  • 9 practice questions
  • 3 graded problems
On this page
  1. Description
  2. The approach: building the LPS array
  3. The search
  4. A second flavor: the table on its own
  5. Where you'll see it
  6. Which tool for which job?
  7. Common mistakes
  8. Check yourself
  9. Key takeaways
  10. Quick revise
  11. Practice

Description

After a mismatch, naive search slides the pattern one position and starts comparing again from its first character. It re-reads text it has already seen, which is what makes it O(n × m). KMP (Knuth-Morris-Pratt) never moves backwards in the text. It precomputes, for every prefix of the pattern, the longest proper prefix that's also a suffix (the LPS array), so after a mismatch it knows how much of what it just matched can be reused. The same table answers questions about palindromes and periods without any search.

We'll go from simple to more technical:

  1. The pattern: build the LPS array, falling back through borders on a mismatch.
  2. The search: one pass over the text, never moving backwards, and overlapping matches.
  3. A second flavor: the table on its own (palindromic prefixes, periods).
  4. What breaks it: resets to zero, off-by-one fallbacks, missing separators.

The approach: building the LPS array

Java

int[] buildLps(String p) {
    int[] lps = new int[p.length()];
    int len = 0;                                   // length of the current border
    for (int i = 1; i < p.length(); ) {
        if (p.charAt(i) == p.charAt(len)) {
            lps[i++] = ++len;                      // the border grows by one
        } else if (len > 0) {
            len = lps[len - 1];                    // fall back to the next-shorter border
        } else {
            lps[i++] = 0;
        }
    }
    return lps;
}

Python

def build_lps(p: str) -> list[int]:
    lps = [0] * len(p)
    length = 0                                       # length of the current border
    i = 1
    while i < len(p):
        if p[i] == p[length]:
            length += 1
            lps[i] = length                          # the border grows by one
            i += 1
        elif length > 0:
            length = lps[length - 1]                 # fall back to the next-shorter border
        else:
            lps[i] = 0
            i += 1
    return lps

lps[i] is the length of the longest proper prefix of p[0..i] that is also a suffix of it - a "border". For "aabaaab" it's 0 1 0 1 2 2 3. On a mismatch, len = lps[len - 1] tries the next-longest border of what has matched so far. Resetting len = 0 instead isn't just slower - it gives wrong values: 0 1 0 1 2 1 0 for "aabaaab", missing the "aa" border at position 5.

It's O(m) in total: len increases by at most one per step of i, and every fallback decreases it, so there can be at most m fallbacks overall.

Java

int countOccurrences(String text, String pattern) {
    int[] lps = buildLps(pattern);
    int count = 0;
    for (int i = 0, j = 0; i < text.length(); i++) {        // j = pattern characters matched
        while (j > 0 && text.charAt(i) != pattern.charAt(j)) j = lps[j - 1];
        if (text.charAt(i) == pattern.charAt(j)) j++;
        if (j == pattern.length()) {
            count++;
            j = lps[j - 1];                                  // keep the overlap for the next match
        }
    }
    return count;
}

Python

def count_occurrences(text: str, pattern: str) -> int:
    lps = build_lps(pattern)
    count = j = 0                                    # j = pattern characters matched
    for ch in text:
        while j > 0 and ch != pattern[j]:
            j = lps[j - 1]
        if ch == pattern[j]:
            j += 1
        if j == len(pattern):
            count += 1
            j = lps[j - 1]                           # keep the overlap for the next match
    return count

The text index only moves forward, so the search is O(n) and the whole algorithm O(n + m). After a full match, continuing from lps[m - 1] reuses the overlap: "aa" occurs 4 times in "aaaaa", and resetting j = 0 would count only 2.

A second flavor: the table on its own

The LPS array answers questions without any search:

  • Longest palindromic prefix. Build it for s + "#" + reverse(s). The last value is the length of the longest prefix of s that is a palindrome, so s.length() minus it is the fewest characters to prepend to make s a palindrome. The # stops a match from running across the join: without it, "aaa" gives an LPS of 5 and an answer of -2.
  • Smallest period. p = n - lps[n - 1]. s is a repetition of a block exactly when p < n and n % p == 0. For "abcabcab", lps ends at 5, so p = 3, but 8 isn't a multiple of 3: not a whole repetition.

Where you'll see it

Because KMP reads each text character exactly once and keeps only O(m) state, it works on streams - network packets, log tails - without buffering the text. Aho-Corasick generalizes its failure links to thousands of patterns at once, which is how intrusion-detection systems such as Snort match attack signatures. The failure function also shows up in DNA analysis, finding periodic repeats.

Which tool for which job?

You needUse
All occurrences, linear worst caseKMP
The same, organized by start positionthe Z-algorithm on pattern + separator + text
Constant work per character (a stream)the KMP automaton (a next-state table)
Overlapping occurrencesKMP, or a lookahead regex (?=pat)
Fewest characters to prepend for a palindromethe LPS of s + "#" + reverse(s)
Smallest periodn - lps[n - 1]

Common mistakes

MistakeWhat happensDo this instead
Resetting to 0 on a mismatchwrong LPS valuesfall back to lps[k - 1]
Resetting to 0 after a matchoverlapping matches missedcontinue from lps[m - 1]
Python str.count or a plain regexcounts non-overlapping matches (2 instead of 7)KMP or a lookahead regex
lps[len] as the fallbackoff by onelps[len - 1]
No separator in s + reverse(s)a border longer than sput "#" between them

Check yourself

Try to answer before opening each one - recalling beats re-reading.

1. What is the LPS array of "abab", and what does lps[3] = 2 mean?

Show answer

0 0 1 2. lps[3] = 2 means the longest proper prefix of "abab" that is also a suffix is "ab", of length 2 - after matching "abab" and then mismatching, the search can continue as if "ab" were already matched.

2. Why is the fallback len = lps[len - 1] rather than len = 0?

Show answer

The next-longest border of the matched part might still extend with the current character. Jumping to 0 skips it and records a value that is too small - "aabaaab" gets lps[5] = 1 instead of 2, and every later value is wrong too.

3. Why does the palindrome-prefix trick need a separator between s and reverse(s)?

Show answer

Without it, a border can run across the join and exceed s's length - for "aaa" the combined string "aaaaaa" has a border of 5, giving an impossible answer of -2. A character that occurs in neither half caps every border at s.length().

Key takeaways

  • lps[i] = the longest proper prefix of p[0..i] that is also its suffix.
  • On a mismatch, fall back to lps[len - 1]; resetting to 0 gives wrong values.
  • The search never moves back in the text: O(n + m) overall.
  • After a match, continue from lps[m - 1] to count overlapping occurrences.
  • The table alone gives the smallest period (n - lps[n - 1]) and, with s + "#" + reverse(s), the longest palindromic prefix.

Quick revise

The whole skill on one page, for the night before an interview.

This page puts the lesson and the deep dive together. Skim it before practice or an interview, and go back to the full lesson for anything you don't recognize.

Pick the tool

You needUse
All matches, linearKMP or Z
O(1) per characterKMP automaton
Overlapping countKMP / (?=pat)
Palindrome prefixLPS of s + "#" + reverse(s)
Periodn - lps[n - 1]

Snippets worth knowing by heart

Java

int[] lps = new int[m];
for (int i = 1, k = 0; i < m; i++) {
    while (k > 0 && p.charAt(i) != p.charAt(k)) k = lps[k - 1];   // fall back through borders
    if (p.charAt(i) == p.charAt(k)) k++;
    lps[i] = k;
}

Python

import re
overlapping = len(re.findall("(?=" + re.escape(pat) + ")", text))   # str.count gives non-overlapping

The traps

  • Resetting to 0 on a mismatch or after a match: LPS values go wrong and overlaps are missed. Fall back to lps[k - 1].
  • str.count for overlapping counts: it counts non-overlapping matches only. Use KMP or a lookahead regex.
  • lps[len] instead of lps[len - 1]: the fallback is off by one. Read the previous entry.
  • No separator: in s + reverse(s) the border can run longer than s. Put "#" between them.

Staff-level points, one line each

  • KMP, Z, the KMP automaton and brute force agreed on 500 random cases.
  • One character cost KMP 999 fallbacks (a × 999 + b against a × 999 + c); the automaton always takes 1 step.
  • "aaaa" in "a" × 10: str.count, findall, Matcher.find give 2; KMP and (?=aaaa) give 7.
  • The automaton costs (m + 1) × alphabet entries.

Say it in an interview

  • "lps[i] is the longest proper prefix of p[0..i] that's also a suffix."
  • "On a mismatch I fall back to lps[k - 1], so the text pointer never moves back."
  • "It's O(n + m) amortized; for a hard per-character bound, the automaton."
  • "Overlapping counts need KMP or a lookahead, not str.count."

Practice

This skill comes with 9 multiple-choice questions that tell you which misconception a wrong answer reveals and 3 graded problems. Your answers are graded and your mastery is tracked, so you'll know when the skill holds up. Running code and saving progress need a free account.

Practice KMP Failure Function Application free

Count Pattern Occurrences (KMP)

MediumProCoding problemJava · Python

Count how many times pattern occurs in text, counting overlapping occurrences (aa occurs 4 times in aaaaa). Use the KMP failure function so the whole search is O(n + m) and never moves backwards in the text.

The full statement, the examples and the hidden test suite are part of AI Proof Engineers Pro.

KMP Algorithm: Build the LPS Array

HardProCoding problemJava · Python

Given a string pattern, build its LPS array: lps[i] is the length of the longest proper prefix of pattern[0..i] that is also a suffix of pattern[0..i] (a "proper" prefix excludes the whole substring itself).

The full statement, the examples and the hidden test suite are part of AI Proof Engineers Pro.

Minimum Characters required to make a String Palindromic

HardProCoding problemJava · Python

Given a string s, return the minimum number of characters that must be added at the front of s to make it a palindrome.

The full statement, the examples and the hidden test suite are part of AI Proof Engineers Pro.

Practice KMP Failure Function Application free

No card needed. Lessons, practice questions and easy problems are free forever.

Free plan, no card · 7-day Pro trial · Cancel anytime