Manacher's Algorithm
Finds the longest palindromic substring (and all palindrome radii) in linear O(n) time by exploiting palindrome symmetry to avoid redundant character comparisons.
Syntax
manacher(s) Parameters
| Name | Type | Required | Description |
|---|---|---|---|
s | string | Yes | The input string. The algorithm inserts separator characters internally to handle both odd- and even-length palindromes uniformly. |
Returns
{ longest: string, start: number, length: number } — The longest palindromic substring with its starting index and length in the original string.
Examples
function manacher(s) {
// Transform: 'abc' → '#a#b#c#'
const t = '#' + s.split('').join('#') + '#';
const n = t.length;
const p = new Array(n).fill(0); // p[i] = palindrome radius at i in t
let c = 0, r = 0; // center and right boundary of rightmost palindrome
for (let i = 0; i < n; i++) {
if (i < r) p[i] = Math.min(r - i, p[2 * c - i]);
while (i - p[i] - 1 >= 0 && i + p[i] + 1 < n && t[i - p[i] - 1] === t[i + p[i] + 1])
p[i]++;
if (i + p[i] > r) { c = i; r = i + p[i]; }
}
// Find max palindrome in original string
let maxLen = 0, center = 0;
for (let i = 0; i < n; i++) if (p[i] > maxLen) { maxLen = p[i]; center = i; }
const start = (center - maxLen) / 2;
return { longest: s.slice(start, start + maxLen), start, length: maxLen };
}
console.log(manacher('babad'));
console.log(manacher('cbbd'));
Output
{ longest: 'bab', start: 0, length: 3 }
{ longest: 'bb', start: 1, length: 2 }
function manacher(s) {
const t = '#' + s.split('').join('#') + '#';
const n = t.length, p = new Array(n).fill(0);
let c = 0, r = 0;
for (let i = 0; i < n; i++) {
if (i < r) p[i] = Math.min(r - i, p[2*c-i]);
while (i-p[i]-1 >= 0 && i+p[i]+1 < n && t[i-p[i]-1] === t[i+p[i]+1]) p[i]++;
if (i+p[i] > r) { c = i; r = i+p[i]; }
}
let mx = 0, ct = 0;
p.forEach((v,i) => { if(v>mx){mx=v;ct=i;} });
const st = (ct-mx)/2;
return { longest: s.slice(st, st+mx), start: st, length: mx };
}
// Entire string is a palindrome
console.log(manacher('racecar'));
Output
{ longest: 'racecar', start: 0, length: 7 }
Notes
| Case | Time | Space |
|---------|------|-------|
| Best | O(n) | O(n) |
| Average | O(n) | O(n) |
| Worst | O(n) | O(n) |
The key insight is the **mirror property**: if i is within the right
boundary r of the current rightmost palindrome centred at c, then p[i]
can be initialised from its mirror p[2c - i] without rescanning.
The transformed string (inserting '#' between every character) unifies
odd and even palindromes, simplifying the implementation. Manacher's is
optimal — any algorithm must read every character at least once.
The p[] array encodes all palindromic substrings, not just the longest.