⚡ Play a quiz
HomeLearnData Structures & AlgorithmsStrings › Pattern matching
Lesson 3 of 3 · Strings

String Pattern Matching: Naive Search and KMP

Strings100%

Finding a pattern inside a text is the most-used string operation there is, and the naive approach throws away information on every mismatch. KMP is the classic fix: precompute what the pattern already told you about itself, so the search never rechecks the same character twice.

Read time
9 min
Track
Data Structures & Algorithms
Sections
3
Practice
4

The naive scan

Naive substring search
int naiveSearch(String text, String pattern) {
    int n = text.length(), m = pattern.length();
    for (int i = 0; i + m <= n; i++) {
        int j = 0;
        while (j < m && text.charAt(i + j) == pattern.charAt(j)) j++;
        if (j == m) return i;      // full match at i
    }
    return -1;
}

Worst case O(n x m) — text "aaaaaaaab" against pattern "aaab" nearly matches at every position and fails on the last character each time. In practice it is often fine; in an interview it is the baseline you improve on.

KMP and the prefix table

The naive version restarts the pattern from scratch after a mismatch. But the characters that already matched are known — and if the pattern's start reappears inside that matched region, part of the work is still valid. The prefix table (also called the failure function or LPS array) records exactly that: for each position, the length of the longest proper prefix that is also a suffix.

Prefix table for pattern "ababaca"
Index0123456
Characterababaca
LPS0012301
Build the prefix table, then search with it
int[] buildLps(String p) {
    int[] lps = new int[p.length()];
    int len = 0;
    for (int i = 1; i < p.length(); ) {
        if (p.charAt(i) == p.charAt(len)) {
            lps[i++] = ++len;
        } else if (len > 0) {
            len = lps[len - 1];          // fall back, do not restart
        } else {
            lps[i++] = 0;
        }
    }
    return lps;
}

int kmpSearch(String text, String pattern) {
    int[] lps = buildLps(pattern);
    int i = 0, j = 0;
    while (i < text.length()) {
        if (text.charAt(i) == pattern.charAt(j)) {
            i++;
            j++;
            if (j == pattern.length()) return i - j;
        } else if (j > 0) {
            j = lps[j - 1];              // reuse the matched prefix
        } else {
            i++;
        }
    }
    return -1;
}

The text index i never moves backwards, so the search is O(n); building the table is O(m). O(n + m) total, O(m) space — and the reason it works is that the pattern's overlap with itself was computed before the search started.

text abab abac a 5 characters matched mismatch naive — restart one position later, recheck from scratch aba baca text index moves back KMP — lps[4] = 3, so slide the pattern by 5 - 3 = 2 aba baca already known to match — never rechecked text index only moves forward → O(n + m) instead of O(n x m)
The naive scan throws away what it already matched; KMP slides the pattern by what the prefix table already knows, so the text index never goes backwards.

The other algorithms, and when they matter

Substring search algorithms
AlgorithmTimeBest for
NaiveO(n x m)Short patterns; readable code
KMPO(n + m)Guaranteed linear worst case
Rabin-KarpO(n + m) averageSearching for many patterns at once
Boyer-MooreSublinear in practiceLong patterns over long text; what grep uses
Built-in indexOf / findImplementation-definedReal production code
tip

In an interview

Write the naive version first and state its complexity. Then say what it wastes and how a prefix table removes that waste. Reaching for KMP immediately, without that framing, tends to read as memorisation.

Key takeaways

  • Naive search is O(n x m) because it rechecks characters it already matched.
  • The prefix table stores, per position, the longest proper prefix that is also a suffix.
  • KMP never moves the text index backwards: O(n + m) time, O(m) space.
  • Rabin-Karp suits many patterns; Boyer-Moore is fastest in practice on long text.

Practice

ProblemPatternLevel
Implement indexOf / strStrNaive or KMPEasy
Repeated substring patternKMP prefix tableMedium
Shortest palindrome by prepending charactersKMP prefix tableHard
Find all anagram start indices in a stringSliding window + countsMedium

Frequently asked questions

What is the KMP algorithm?

Knuth-Morris-Pratt is a substring search that precomputes a prefix table for the pattern, so that after a mismatch it can skip ahead using what already matched instead of restarting. It runs in O(n + m).

What is the LPS array in KMP?

For every position in the pattern it stores the length of the longest proper prefix of the pattern that is also a suffix ending at that position. That value is how far the pattern can shift without missing a match.

Is KMP always faster than the naive approach?

It is better in the worst case, but the naive scan is often faster on short patterns and random text because it has no setup cost and better cache behaviour. KMP's value is the guarantee.

Test yourself on Strings

Reading is not recall. Take a timed quiz on this topic solo, or share a room code and battle friends on it.

⚡ Start the Strings quiz

More in Strings