Builds on Hash Map, which you haven’t finished yet.
Knuth-Morris-Pratt (KMP) String Matching
You get two strings, a text haystack and a word needle. Look for the places where needle appears in haystack as a block of consecutive characters, and return the index in haystack where the leftmost of them begins.
If needle appears nowhere in haystack, return -1.
0; the second copy of "sad", at index 6, is never reached.KMP
- The pattern may appear with its letters in any order, as in Find All Anagrams in a String (LC 438): order doesn't matter, so a window of letter counts, not a prefix of the pattern, decides a match. See Fixed Window
| 1 | def str_str(haystack: str, needle: str) -> int: |
| 2 | """KMP: find needle in haystack without ever moving back in haystack. |
| 3 | Time: O(N + M) | Space: O(M) |
| 4 | """ |
| 5 | m = len(needle) |
| 6 | # lps[i]: the longest proper prefix of needle[:i + 1] that is also its suffix (its longest border). |
| 7 | lps = [0] * m |
| 8 | k = 0 # Length of the prefix of needle matched so far |
| 9 | Â |
| 10 | # 1. Build The Border Table From needle Against Itself: |
| 11 | # Building lps runs the same search on needle against itself, so a mismatch falls back to a |
| 12 | # shorter border the same way the main search below does. |
| 13 | for i in range(1, m): |
| 14 | while k > 0 and needle[i] != needle[k]: |
| 15 | k = lps[k - 1] |
| 16 | if needle[i] == needle[k]: |
| 17 | k += 1 |
| 18 | lps[i] = k |
| 19 | Â |
| 20 | # 2. Scan haystack Once, Falling Back Instead Of Restarting: |
| 21 | # On a mismatch, the k letters just matched are needle[:k], already known, so k falls back |
| 22 | # to the longest border that still fits; the match ends at i, so it starts i - m + 1 letters |
| 23 | # earlier. |
| 24 | k = 0 |
| 25 | for i, ch in enumerate(haystack): |
| 26 | while k > 0 and ch != needle[k]: |
| 27 | k = lps[k - 1] |
| 28 | if ch == needle[k]: |
| 29 | k += 1 |
| 30 | if k == m: |
| 31 | return i - m + 1 |
| 32 | return -1 |
haystack[i], k is the length of the longest prefix of needle that ends at i, and lps[j] is the longest proper prefix of needle[:j + 1] that is also its suffix.KMP never reads a character of the text twice: when a mismatch comes after k matched letters, those letters are needle[:k], which you already know, so the search keeps their longest border, k = lps[k - 1], and tries the same letter again instead of moving the start forward by one and re-reading. It is correct because a match that is still possible must begin at a suffix of what was read that is also a prefix of needle, and lps[k - 1] is the longest such part; falling back again while k > 0 and ch != needle[k] visits every shorter one in order, and lps itself is built by running the same loop on needle against itself. The trap is resetting k = 0 on a mismatch: on haystack = "abababc", needle = "ababc" it throws away the border "ab" at i = 4, where the only match begins, and returns -1 instead of 2.
See it run
KMP String Matching, step by step
i -k 0lps 0
The top row is haystack. The middle row is needle. The bottom row is lps, the longest border of each needle prefix.
ithe letter being readkthe next needle letter to compare; k letters of needle are already matchedborderthe lps place that was just written or just read
Start: needle has 5 letters, so lps has 5 places. lps is built first, then haystack is scanned once.
Gotcha 1 of 4
- The trap
Gotcha 1: Resetting
k = 0on a mismatchMistakewhile k > 0 and ch != needle[k]: k = 0Fixwhile k > 0 and ch != needle[k]: k = lps[k - 1]What happens: After
kmatched letters, the text just read isneedle[:k]. Its end can also be the start ofneedle. A match can then begin inside the failed attempt.k = 0throws that start away, and the search can miss the match.k = lps[k - 1]keeps it.Example: haystack = "abababc", needle = "ababc"Mistake: returns -1Fix: returns 2
Deep Dive
A mismatch must not send the search backward
The simple search tries every start position in haystack. At each start it compares letters until one differs, then it moves the start forward by one and compares again from the first letter of needle. It reads the same letters of haystack many times. On haystack = "aaaaaaaa" and needle = "aaab" it makes 20 comparisons, and the cost grows to O(N * M) for longer inputs.
KMP (Knuth-Morris-Pratt) never moves its position in haystack backward. Suppose k letters matched and the next letter differs. The k letters just read are exactly needle[:k], and you already know them. You do not need to read them again.
A new match can still start inside those k letters. It must start at a suffix of needle[:k] that is also a prefix of needle. A part of a string that is both a prefix and a suffix of it is called a border. The longest border of needle[:k] is lps[k - 1], so the search sets k = lps[k - 1] and compares the same letter of haystack again.
If that letter still differs, the loop while k > 0 and ch != needle[k] falls back again, to the next shorter border. The loop visits every possible start in order from the longest, and it stops at the first one that fits.
Building lps with the same loop
lps[i] is the length of the longest border of needle[:i + 1] that is shorter than the whole prefix. For needle = "ababc" the table is [0, 0, 1, 2, 0]. At i = 3 the prefix "abab" has the border "ab", so lps[3] = 2.
The table is built by searching needle inside itself. The loop starts at i = 1. lps[0] stays 0, because a single letter has no shorter border. For each i, k is the length of the border of needle[:i]. If needle[i] == needle[k], the border grows by one letter and k += 1. If not, k falls back to lps[k - 1] and the same letter is compared again.
Because the build and the search are the same loop, one idea explains both. k is always the length of the longest prefix of needle that ends at the letter just read.
Find "ababc" in "abababc", traced
Take haystack = "abababc" and needle = "ababc". The build gives lps = [0, 0, 1, 2, 0]. At i = 4 the letter "c" differs from needle[2] = "a", so k falls back to lps[1] = 0, and "c" matches nothing.
The search reads "abab" and k grows to 4. At i = 4 the letter "a" differs from needle[4] = "c". The border of "abab" is "ab", so k = lps[3] = 2. The letter "a" then matches needle[2], and k = 3.
At i = 5 the letter "b" gives k = 4. At i = 6 the letter "c" gives k = 5 = m. The match ends at i = 6, so it starts at 6 - 5 + 1 = 2.
The board marks the trap at i = 4. With k = 0 instead of k = lps[3], the search forgets that "ab" is already matched. At i = 6 the letter "c" is then compared with needle[2], the match is lost, and the result is -1 instead of 2.
Why the cost is O(N + M)
In the search, each letter of haystack increases k by at most 1. Each fall back makes k smaller. So the total number of fall backs cannot be more than the total number of increases, which is at most N. The scan costs O(N).
The build follows the same argument on needle, so it costs O(M). The total time is O(N + M). The only extra memory is lps, so the space is O(M).
The same table solves other problems. lps[m - 1] is the length of the longest border of the whole string, which is the answer to Longest Happy Prefix (LC 1392). A string of length m is a repeated block when p = m - lps[m - 1] is smaller than m and divides m, as in Repeated Substring Pattern (LC 459).
60-Second Pitch
"The simple search re-reads haystack after every mismatch, which costs O(N * M). KMP never goes back. When k letters matched and the next one differs, those k letters are needle[:k], which I already know. So I keep their longest border, k = lps[k - 1], and compare the same letter again. The table lps stores the longest border of each prefix of needle. I build it with the same loop, searching needle inside itself. Then one scan of haystack with a while fall back returns i - m + 1 when k reaches m. Each letter raises k by at most one and each fall back lowers it, so the time is O(N + M) and the space is O(M). The same table gives Longest Happy Prefix as lps[m - 1]."
Common Questions
- Why is the time O(N + M) and not O(N * M)?
kgoes up by at most 1 for each letter ofhaystack, and every fall back lowers it. The number of fall backs therefore cannot exceed the number of increases. The scan makes at most about2Nsteps, and the build makes at most about2Msteps.- Why
lps[k - 1]and notlps[k]? lps[i]describesneedle[:i + 1]. Afterkmatched letters, the text read isneedle[:k], so its border is at indexk - 1.lps[k]describes a prefix one letter longer than what was read, and thewhileloop can then run forever.- How do you find every occurrence, not only the first?
When
k == m, recordi - m + 1and then setk = lps[m - 1]instead of returning. The next match may overlap this one. Onhaystack = "aaaaa"andneedle = "aa"this finds the starts[0, 1, 2, 3].- How does the same table solve Longest Happy Prefix (LC 1392)?
A happy prefix is a non-empty prefix that is also a suffix and is shorter than the whole string. That is a border, so the answer is
s[:lps[m - 1]]. Fors = "level"the table ends in 1, and the answer is"l".- Why not use a hash of each window instead?
A rolling hash (Rabin-Karp) compares one number per window and is fast on average. Two different windows can have the same hash, so every hash match needs a letter-by-letter check. KMP has no such case, and its
O(N + M)time holds for every input.
SWE Reasoning
Spot it
- You must find an exact pattern in a long text in
O(N + M), as in Find the Index of the First Occurrence in a String (LC 28). - The question is about a prefix of a string that comes back later in it: a border, a period or a repeated start (Longest Happy Prefix (LC 1392), Repeated Substring Pattern (LC 459), Shortest Palindrome (LC 214)).
- The simple search re-reads letters after a mismatch, and the input is large enough for
O(N * M)to be too slow. - Letters must match in order. If the pattern may appear with its letters in any order, this is a window of letter counts instead.
The invariant, step by step
lpsis built first.lps[j]is the length of the longest border ofneedle[:j + 1]that is shorter than that prefix.- The search starts with
k = 0.kis the number of letters ofneedlematched at the end of what was read. - For each letter
chofhaystack,kfalls back withk = lps[k - 1]whilek > 0 and ch != needle[k]. Each fall back keeps the longest part that can still start a match. - If
ch == needle[k], thenk += 1. After this step,kis the length of the longest prefix ofneedlethat ends atch. - When
k == m, the wholeneedleends ati, so it starts ati - m + 1. Ifhaystackends first, the answer is-1.
Traps
- Resetting
k = 0on a mismatch. The search forgets a border that was already matched. Onhaystack = "abababc"andneedle = "ababc"it returns-1instead of2. - Building
lpswithrange(m). The loop comparesneedle[0]with itself, setslps[0] = 1, and a later fall back never ends. - Using
ifinstead ofwhilefor the fall back. After one fall back the letter may still differ, sokthen counts letters that were never matched. - Using
lps[k]instead oflps[k - 1].lps[i]describesneedle[:i + 1], so the border of thekletters read is atlps[k - 1].
Practice
Builds on Hash Map, which you haven’t finished yet.
Knuth-Morris-Pratt (KMP) String Matching
You get two strings, a text haystack and a word needle. Look for the places where needle appears in haystack as a block of consecutive characters, and return the index in haystack where the leftmost of them begins.
If needle appears nowhere in haystack, return -1.
0; the second copy of "sad", at index 6, is never reached.KMP
- The pattern may appear with its letters in any order, as in Find All Anagrams in a String (LC 438): order doesn't matter, so a window of letter counts, not a prefix of the pattern, decides a match. See Fixed Window
| 1 | def str_str(haystack: str, needle: str) -> int: |
| 2 | """KMP: find needle in haystack without ever moving back in haystack. |
| 3 | Time: O(N + M) | Space: O(M) |
| 4 | """ |
| 5 | m = len(needle) |
| 6 | # lps[i]: the longest proper prefix of needle[:i + 1] that is also its suffix (its longest border). |
| 7 | lps = [0] * m |
| 8 | k = 0 # Length of the prefix of needle matched so far |
| 9 | Â |
| 10 | # 1. Build The Border Table From needle Against Itself: |
| 11 | # Building lps runs the same search on needle against itself, so a mismatch falls back to a |
| 12 | # shorter border the same way the main search below does. |
| 13 | for i in range(1, m): |
| 14 | while k > 0 and needle[i] != needle[k]: |
| 15 | k = lps[k - 1] |
| 16 | if needle[i] == needle[k]: |
| 17 | k += 1 |
| 18 | lps[i] = k |
| 19 | Â |
| 20 | # 2. Scan haystack Once, Falling Back Instead Of Restarting: |
| 21 | # On a mismatch, the k letters just matched are needle[:k], already known, so k falls back |
| 22 | # to the longest border that still fits; the match ends at i, so it starts i - m + 1 letters |
| 23 | # earlier. |
| 24 | k = 0 |
| 25 | for i, ch in enumerate(haystack): |
| 26 | while k > 0 and ch != needle[k]: |
| 27 | k = lps[k - 1] |
| 28 | if ch == needle[k]: |
| 29 | k += 1 |
| 30 | if k == m: |
| 31 | return i - m + 1 |
| 32 | return -1 |
haystack[i], k is the length of the longest prefix of needle that ends at i, and lps[j] is the longest proper prefix of needle[:j + 1] that is also its suffix.KMP never reads a character of the text twice: when a mismatch comes after k matched letters, those letters are needle[:k], which you already know, so the search keeps their longest border, k = lps[k - 1], and tries the same letter again instead of moving the start forward by one and re-reading. It is correct because a match that is still possible must begin at a suffix of what was read that is also a prefix of needle, and lps[k - 1] is the longest such part; falling back again while k > 0 and ch != needle[k] visits every shorter one in order, and lps itself is built by running the same loop on needle against itself. The trap is resetting k = 0 on a mismatch: on haystack = "abababc", needle = "ababc" it throws away the border "ab" at i = 4, where the only match begins, and returns -1 instead of 2.
See it run
KMP String Matching, step by step
i -k 0lps 0
The top row is haystack. The middle row is needle. The bottom row is lps, the longest border of each needle prefix.
ithe letter being readkthe next needle letter to compare; k letters of needle are already matchedborderthe lps place that was just written or just read
Start: needle has 5 letters, so lps has 5 places. lps is built first, then haystack is scanned once.
Gotcha 1 of 4
- The trap
Gotcha 1: Resetting
k = 0on a mismatchMistakewhile k > 0 and ch != needle[k]: k = 0Fixwhile k > 0 and ch != needle[k]: k = lps[k - 1]What happens: After
kmatched letters, the text just read isneedle[:k]. Its end can also be the start ofneedle. A match can then begin inside the failed attempt.k = 0throws that start away, and the search can miss the match.k = lps[k - 1]keeps it.Example: haystack = "abababc", needle = "ababc"Mistake: returns -1Fix: returns 2
Deep Dive
A mismatch must not send the search backward
The simple search tries every start position in haystack. At each start it compares letters until one differs, then it moves the start forward by one and compares again from the first letter of needle. It reads the same letters of haystack many times. On haystack = "aaaaaaaa" and needle = "aaab" it makes 20 comparisons, and the cost grows to O(N * M) for longer inputs.
KMP (Knuth-Morris-Pratt) never moves its position in haystack backward. Suppose k letters matched and the next letter differs. The k letters just read are exactly needle[:k], and you already know them. You do not need to read them again.
A new match can still start inside those k letters. It must start at a suffix of needle[:k] that is also a prefix of needle. A part of a string that is both a prefix and a suffix of it is called a border. The longest border of needle[:k] is lps[k - 1], so the search sets k = lps[k - 1] and compares the same letter of haystack again.
If that letter still differs, the loop while k > 0 and ch != needle[k] falls back again, to the next shorter border. The loop visits every possible start in order from the longest, and it stops at the first one that fits.
Building lps with the same loop
lps[i] is the length of the longest border of needle[:i + 1] that is shorter than the whole prefix. For needle = "ababc" the table is [0, 0, 1, 2, 0]. At i = 3 the prefix "abab" has the border "ab", so lps[3] = 2.
The table is built by searching needle inside itself. The loop starts at i = 1. lps[0] stays 0, because a single letter has no shorter border. For each i, k is the length of the border of needle[:i]. If needle[i] == needle[k], the border grows by one letter and k += 1. If not, k falls back to lps[k - 1] and the same letter is compared again.
Because the build and the search are the same loop, one idea explains both. k is always the length of the longest prefix of needle that ends at the letter just read.
Find "ababc" in "abababc", traced
Take haystack = "abababc" and needle = "ababc". The build gives lps = [0, 0, 1, 2, 0]. At i = 4 the letter "c" differs from needle[2] = "a", so k falls back to lps[1] = 0, and "c" matches nothing.
The search reads "abab" and k grows to 4. At i = 4 the letter "a" differs from needle[4] = "c". The border of "abab" is "ab", so k = lps[3] = 2. The letter "a" then matches needle[2], and k = 3.
At i = 5 the letter "b" gives k = 4. At i = 6 the letter "c" gives k = 5 = m. The match ends at i = 6, so it starts at 6 - 5 + 1 = 2.
The board marks the trap at i = 4. With k = 0 instead of k = lps[3], the search forgets that "ab" is already matched. At i = 6 the letter "c" is then compared with needle[2], the match is lost, and the result is -1 instead of 2.
Why the cost is O(N + M)
In the search, each letter of haystack increases k by at most 1. Each fall back makes k smaller. So the total number of fall backs cannot be more than the total number of increases, which is at most N. The scan costs O(N).
The build follows the same argument on needle, so it costs O(M). The total time is O(N + M). The only extra memory is lps, so the space is O(M).
The same table solves other problems. lps[m - 1] is the length of the longest border of the whole string, which is the answer to Longest Happy Prefix (LC 1392). A string of length m is a repeated block when p = m - lps[m - 1] is smaller than m and divides m, as in Repeated Substring Pattern (LC 459).
60-Second Pitch
"The simple search re-reads haystack after every mismatch, which costs O(N * M). KMP never goes back. When k letters matched and the next one differs, those k letters are needle[:k], which I already know. So I keep their longest border, k = lps[k - 1], and compare the same letter again. The table lps stores the longest border of each prefix of needle. I build it with the same loop, searching needle inside itself. Then one scan of haystack with a while fall back returns i - m + 1 when k reaches m. Each letter raises k by at most one and each fall back lowers it, so the time is O(N + M) and the space is O(M). The same table gives Longest Happy Prefix as lps[m - 1]."
Common Questions
- Why is the time O(N + M) and not O(N * M)?
kgoes up by at most 1 for each letter ofhaystack, and every fall back lowers it. The number of fall backs therefore cannot exceed the number of increases. The scan makes at most about2Nsteps, and the build makes at most about2Msteps.- Why
lps[k - 1]and notlps[k]? lps[i]describesneedle[:i + 1]. Afterkmatched letters, the text read isneedle[:k], so its border is at indexk - 1.lps[k]describes a prefix one letter longer than what was read, and thewhileloop can then run forever.- How do you find every occurrence, not only the first?
When
k == m, recordi - m + 1and then setk = lps[m - 1]instead of returning. The next match may overlap this one. Onhaystack = "aaaaa"andneedle = "aa"this finds the starts[0, 1, 2, 3].- How does the same table solve Longest Happy Prefix (LC 1392)?
A happy prefix is a non-empty prefix that is also a suffix and is shorter than the whole string. That is a border, so the answer is
s[:lps[m - 1]]. Fors = "level"the table ends in 1, and the answer is"l".- Why not use a hash of each window instead?
A rolling hash (Rabin-Karp) compares one number per window and is fast on average. Two different windows can have the same hash, so every hash match needs a letter-by-letter check. KMP has no such case, and its
O(N + M)time holds for every input.
SWE Reasoning
Spot it
- You must find an exact pattern in a long text in
O(N + M), as in Find the Index of the First Occurrence in a String (LC 28). - The question is about a prefix of a string that comes back later in it: a border, a period or a repeated start (Longest Happy Prefix (LC 1392), Repeated Substring Pattern (LC 459), Shortest Palindrome (LC 214)).
- The simple search re-reads letters after a mismatch, and the input is large enough for
O(N * M)to be too slow. - Letters must match in order. If the pattern may appear with its letters in any order, this is a window of letter counts instead.
The invariant, step by step
lpsis built first.lps[j]is the length of the longest border ofneedle[:j + 1]that is shorter than that prefix.- The search starts with
k = 0.kis the number of letters ofneedlematched at the end of what was read. - For each letter
chofhaystack,kfalls back withk = lps[k - 1]whilek > 0 and ch != needle[k]. Each fall back keeps the longest part that can still start a match. - If
ch == needle[k], thenk += 1. After this step,kis the length of the longest prefix ofneedlethat ends atch. - When
k == m, the wholeneedleends ati, so it starts ati - m + 1. Ifhaystackends first, the answer is-1.
Traps
- Resetting
k = 0on a mismatch. The search forgets a border that was already matched. Onhaystack = "abababc"andneedle = "ababc"it returns-1instead of2. - Building
lpswithrange(m). The loop comparesneedle[0]with itself, setslps[0] = 1, and a later fall back never ends. - Using
ifinstead ofwhilefor the fall back. After one fall back the letter may still differ, sokthen counts letters that were never matched. - Using
lps[k]instead oflps[k - 1].lps[i]describesneedle[:i + 1], so the border of thekletters read is atlps[k - 1].