KMP (Knuth-Morris-Pratt) Algorithm
Finds all occurrences of a pattern in a text in O(n + m) by precomputing a failure function that avoids redundant character comparisons on mismatch.
Syntax
kmpSearch(text, pattern) Parameters
| Name | Type | Required | Description |
|---|---|---|---|
text | string | Yes | The string to search within (length n). |
pattern | string | Yes | The pattern to search for (length m). Must be non-empty. |
Returns
number[] — Array of starting indices (0-based) where pattern occurs in text.
Examples
function buildLPS(pattern) {
const lps = new Array(pattern.length).fill(0);
let len = 0, i = 1;
while (i < pattern.length) {
if (pattern[i] === pattern[len]) { lps[i++] = ++len; }
else if (len > 0) { len = lps[len - 1]; }
else { lps[i++] = 0; }
}
return lps;
}
function kmpSearch(text, pattern) {
const lps = buildLPS(pattern);
const matches = [];
let i = 0, j = 0;
while (i < text.length) {
if (text[i] === pattern[j]) { i++; j++; }
if (j === pattern.length) { matches.push(i - j); j = lps[j - 1]; }
else if (i < text.length && text[i] !== pattern[j]) {
if (j > 0) j = lps[j - 1]; else i++;
}
}
return matches;
}
console.log(kmpSearch('AABAACAADAABAABA', 'AABA'));
Output
[ 0, 9, 12 ]
// Pattern not found
function buildLPS(p) {
const lps=new Array(p.length).fill(0); let len=0,i=1;
while(i<p.length){ if(p[i]===p[len]){lps[i++]=++len;}else if(len>0){len=lps[len-1];}else{lps[i++]=0;} } return lps;
}
function kmpSearch(text, pattern) {
const lps=buildLPS(pattern); const matches=[]; let i=0,j=0;
while(i<text.length){ if(text[i]===pattern[j]){i++;j++;} if(j===pattern.length){matches.push(i-j);j=lps[j-1];} else if(i<text.length&&text[i]!==pattern[j]){if(j>0)j=lps[j-1];else i++;} }
return matches;
}
console.log(kmpSearch('hello world', 'xyz'));
Output
[]
Notes
| Case | Time | Space |
|---------|-----------|-------|
| Best | O(n + m) | O(m) |
| Average | O(n + m) | O(m) |
| Worst | O(n + m) | O(m) |
The failure function (LPS — Longest Proper Prefix which is also Suffix)
is the key insight: on a mismatch at position j, instead of resetting
j = 0, we jump to j = lps[j-1] — reusing the work already done. This
guarantees the text pointer i never moves backward. KMP is preferred
over the naïve O(nm) approach for large texts or repeated queries. For
multiple patterns, Aho-Corasick generalises KMP with a trie.