1. Home
  2. Design & Analysis of Algorithms
  3. KMP String Matching

KMP String Matching

Find a pattern in a text without ever reading a character twice. The LPS table tells the pattern how far it can jump after a mismatch.

Interactive 3DAdvanced13 min readDAAUpdated

Drag to rotate · Right-drag to pan · Click, then scroll to zoom · Space play · ←→ step

What's happening

Pseudocode

    Try this in the 3D model

    • Press Build LPS table for ABABCABAB. Why is lps[8] = 4?
    • Press Search. Watch the text pointer i. Does it ever move left?
    • At the first mismatch, how many places does the pattern jump, and which lps value decides that?
    • Choose the AABA example. How does KMP find the overlapping matches?

    The problem

    Find every place where a pattern P (length m) occurs inside a text T (length n). Your browser does this with Ctrl + F, editors use it in search-and-replace, and biologists use it to find DNA sequences.

    The naive method tries the pattern at every position. After a mismatch it shifts the pattern by one and starts comparing again from the pattern’s first character, re-reading text it has already seen. With text AAAAAAAAAB and pattern AAAAB it does nearly n × m comparisons.

    The KMP idea

    When a mismatch happens after j characters matched, we already know those j characters: they are P[0…j−1]. Part of them may also be the start of the pattern, and then we don’t need to compare them again.

    The LPS table (longest proper prefix that is also a suffix, also called the failure function) stores this for every prefix of the pattern:

    k 0 1 2 3 4 5 6 7 8
    P[k] A B A B C A B A B
    lps[k] 0 0 1 2 0 1 2 3 4

    lps[8] = 4 because ABAB is both a prefix and a suffix of ABABCABAB.

    Searching with the table

    Keep two pointers: i in the text and j in the pattern.

    • Match: move both right. If j reaches m, a match ends at i, so record it and set j ← lps[m − 1] to keep looking.
    • Mismatch with j > 0: set j ← lps[j − 1]. The pattern slides forward by j − lps[j − 1] places, and i stays where it is.
    • Mismatch with j = 0: move i one step right.

    Because i never moves backwards, the search takes at most about 2n comparisons. On the first example in the 3D model, KMP needs 23 comparisons where the naive method needs 29. The gap grows quickly on repetitive text.

    Building the LPS table

    The table is built the same way, with the pattern matched against itself. len is the length of the current border:

    • If P[i] = P[len]: the border grows, so lps[i] = len + 1.
    • Otherwise, if len > 0: fall back to a shorter border with len ← lps[len − 1], and try again.
    • Otherwise lps[i] = 0.

    This also takes only O(m) time.

    Code

    def build_lps(p):
        lps = [0] * len(p)
        length, i = 0, 1                    # length of the current border
        while i < len(p):
            if p[i] == p[length]:
                length += 1
                lps[i] = length
                i += 1
            elif length:
                length = lps[length - 1]    # try a shorter border
            else:
                lps[i] = 0
                i += 1
        return lps
    
    def kmp_search(text, p):
        lps, found = build_lps(p), []
        i = j = 0
        while i < len(text):
            if text[i] == p[j]:
                i += 1
                j += 1
                if j == len(p):
                    found.append(i - j)
                    j = lps[j - 1]          # allow overlapping matches
            elif j:
                j = lps[j - 1]              # slide the pattern, keep i
            else:
                i += 1
        return found
    
    print(build_lps("ABABCABAB"))                      # [0, 0, 1, 2, 0, 1, 2, 3, 4]
    print(kmp_search("ABABDABACDABABCABAB", "ABABCABAB"))   # [10]
    print(kmp_search("AABAACAADAABAABA", "AABA"))           # [0, 9, 12]
    #include <iostream>
    #include <string>
    #include <vector>
    using namespace std;
    
    vector<int> buildLps(const string& p) {
        vector<int> lps(p.size(), 0);
        for (size_t i = 1, len = 0; i < p.size();) {
            if (p[i] == p[len]) lps[i++] = ++len;
            else if (len) len = lps[len - 1];
            else lps[i++] = 0;
        }
        return lps;
    }
    
    vector<int> kmpSearch(const string& t, const string& p) {
        vector<int> lps = buildLps(p), found;
        for (size_t i = 0, j = 0; i < t.size();) {
            if (t[i] == p[j]) {
                i++; j++;
                if (j == p.size()) { found.push_back(i - j); j = lps[j - 1]; }
            } else if (j) j = lps[j - 1];
            else i++;
        }
        return found;
    }
    
    int main() {
        for (int pos : kmpSearch("AABAACAADAABAABA", "AABA")) cout << pos << " ";   // 0 9 12
    }

    KMP vs other string-matching algorithms

    Algorithm Preprocessing Search (worst case) Notes
    Naive none O(n · m) Simple; fine for short patterns
    KMP O(m) O(n) Never re-reads the text; good for streams
    Rabin–Karp O(m) O(n · m), O(n + m) on average Rolling hash; great for many patterns at once
    Boyer–Moore O(m + σ) O(n · m), often sub-linear Skips ahead from the right; used by grep
    Aho–Corasick O(total pattern length) O(n + matches) Many patterns at once, built on a trie

    KMP’s table is really a small automaton: each state is “how much of the pattern has matched”, and that links it to finite automata.

    Common mistakes

    • Moving i backwards after a mismatch. That throws away the whole point of KMP.
    • Using lps[j] instead of lps[j − 1] after a mismatch at position j.
    • Forgetting to reset j ← lps[m − 1] after a full match, which misses overlapping matches like AABA in AABAABA.
    • Counting the whole string as its own border. The prefix must be proper, shorter than the string.

    Complexity at a glance

    Case / operationTimeWhy
    Building the LPS tableO(m)m = length of the pattern.
    Searching the textO(n)At most 2n comparisons; i never moves back.
    Naive search, worst caseO(n · m)Re-reads text after every mismatch.
    Extra spaceO(m) for the LPS table

    Quick check

    Test yourself — pick an answer to see if you got it.

    1. What does lps[k] store?

    2. What is the LPS table of the pattern AAAA?

    3. After a mismatch at pattern position j > 0, what does KMP do?

    4. What is the time complexity of KMP for a text of length n and a pattern of length m?

    Saved only in this browser — no account needed.
    Spotted a mistake or a bug in the 3D model?

    Report a mistake

    in KMP String Matching. Thank you — every report makes the lesson better for the next reader.

    We'll also include a link to the step of the 3D model you're on and your browser type, so we can reproduce it.