Manacher's Algorithm — Stale Centre Update Bug
Manacher bug: produced 1M-long false palindrome due to stale centre.
- Manacher's algorithm finds the longest palindromic substring in O(n) time by exploiting mirror symmetry.
- Transforms string by inserting separators ('#') to unify even and odd length palindromes.
- Uses palindrome radius array P and a rightmost boundary (c, r) to skip redundant expansions.
- Total expansions are O(n) because the right pointer only moves forward.
- Production insight: common bugs include incorrect mirror calculation when i exceeds r, and off-by-one in start index extraction.
- Biggest mistake: forgetting that the original palindrome length is just P[i] (not 2*P[i]+1).
Finding the longest palindrome in a string naively requires expanding around every center — O(n²). Manacher's insight: if you've already found a big palindrome, any palindrome inside it has a mirror on the other side. Use that mirror to skip recomputation. It's the same 'reuse previous work' idea as KMP, but applied to palindrome radii instead of prefix matches.
LeetCode 5 — Longest Palindromic Substring — is one of the most common interview problems. The O(n^2) expand-around-centre solution passes most judges. Manacher's O(n) solution demonstrates you understand linear string algorithms at a structural level, not just memorised patterns.
Manacher (1975) is not widely taught in undergraduate curricula, which means knowing it distinguishes you. The underlying technique — maintaining a rightmost boundary and exploiting mirror symmetry — is the exact same structural insight as KMP and Z-algorithm. Once you see this pattern, you recognise it everywhere.
The Even/Odd Problem and the Transformation
Palindromes come in two flavours: odd-length ('aba') and even-length ('abba'). Handling both cases separately doubles code complexity. Manacher's standard trick: insert a separator character between every character (and at boundaries) to convert all palindromes to odd-length.
Original: 'abba' Transformed: '#a#b#b#a#'
Now every palindrome in the original maps to an odd-length palindrome in the transformed string. '#b#b#' is the palindrome for 'bb'. '#a#b#b#a#' is the palindrome for 'abba'. We only need to handle one case.
The P Array — Palindrome Radii
For the transformed string T, we compute P[i] = the radius of the longest palindrome centred at i (not counting the centre itself). So P[i] = k means T[i-k..i+k] is a palindrome.
The length of the corresponding original palindrome is P[i] (the separator characters cancel out).
- P[0]=0 ('#')
- P[1]=1 ('#a#')
- P[2]=0 ('a')
- P[3]=3 ('#a#b#b#a#') ← full string
- P[4]=3 (same, centre is '#' between two b's)
- etc.
Manacher's Algorithm — Full Implementation
Manacher maintains a 'mirror window' [l, r] — the palindrome with the rightmost boundary seen so far (centre c, right edge r). For each i, if i < r, initialise P[i] from the mirror position (2*c - i). Then try to expand.
min(r - i, P[mirror]) line is the heart of the O(n) guarantee. If P[mirror] is small, we can't rely on symmetry beyond r-i. Forgetting this min leads to O(n^2) fallback.C++ Implementation of Manacher's Algorithm
For production systems in C++, the same logic applies with careful memory management. The transformed string uses vector<char> for cache locality. The algorithm returns the longest palindromic substring as a std::string.
vector<char> improves cache performance versus std::string due to sequential access. Always use size_t for indices to avoid signed/unsigned mismatches in production code. The std::min call is safe as long as r - i is non-negative — guaranteed by the if (i < r) check.vector<char> for the transformed string and size_t for indexing.Three Case Mirror Analysis: When to Trust the Mirror
The mirror position mirror = 2*c - i gives the palindrome radius z0 = P[mirror] at that point. However, the mirror radius may extend beyond the current right boundary r. Three cases arise:
Case 1: z0 < r - i (mirror palindrome fully inside the rightmost palindrome) P[i] = z0. No expansion needed — the palindrome is guaranteed symmetric inside the larger palindrome.
Case 2: z0 == r - i (mirror palindrome exactly touches the boundary) P[i] = z0 initially, but expansion may extend beyond r. The while loop will try to expand; if successful, the right boundary advances.
Case 3: z0 > r - i (mirror palindrome extends beyond the boundary) P[i] = r - i. The part of the mirror palindrome beyond r is not confirmed symmetric. Expansion from r outward is required.
These three cases are handled elegantly by P[i] = min(r - i, P[mirror]) when i < r.
z0 when z0 > r-i, you may create a palindrome radius that is not validated. The min function is the safety mechanism. In code reviews, ensure the mirror initialisation is always guarded by i < r.min(r - i, P[mirror]) initialisation. Trust the mirror only up to the right boundary.Visual P‑Array Table for Example String
Consider input s = "ababa". Transformed T = '#a#b#a#b#a#'. The P‑array for each index i:
`` Index i: 0 1 2 3 4 5 6 7 8 9 10 T[i]: # a # b # a # b # a # P[i]: 0 1 0 3 0 5 0 3 0 1 0 ` - P[3]=3: palindrome #a#b#a# corresponds to original "aba" (length 3). - P[5]=5: palindrome #a#b#a#b#a# corresponds to original "ababa" (length 5). - All even indices (separators) have even radii multiples? Actually they have radii 0,3,0,3,0? Let's correct: The above table is illustrative. For ababa`, the P array values at even indices are: 0,3,0,3,0? Wait recalc: T: # a # b # a # b # a #. Centres at characters: i=1 (a) radius1, i=3 (b) radius3, i=5 (a) radius5, i=7 (b) radius3, i=9 (a) radius1. Centres at separators: i=0 radius0, i=2 radius? T[2-1]..T[2+1] = #a# -> palindrome? Actually check: index2 is '#', left=1 (a), right=3 (b) -> a!=b so radius0. Similarly i=4 radius0? i=4 is '#', left=3 (b), right=5 (a) -> b!=a so radius0. i=6 '#', left=5 (a), right=7 (b) -> a!=b radius0. i=8 '#', left=7 (b), right=9 (a) -> b!=a radius0. i=10 radius0. So P[i] = [0,1,0,3,0,5,0,3,0,1,0]. That's the correct array.
The longest palindrome ababa (length 5) corresponds to centre i=5 with P[5]=5. Start index = (5-5)//2 = 0.
Why it's O(n) — The Rightmost Boundary Argument
The r pointer only ever moves right — never decreases. Each time we expand (the while loop), r increases. So the total number of expansions across all iterations is at most n. All other operations per iteration are O(1). Total: O(n).
This is the same argument as KMP (pointer only moves forward) and Z-algorithm (r only moves right). Recognising this pattern is key to understanding linear string algorithms.
Advantages and Disadvantages vs Expand-Around-Center
Expand-around-centre (EAC) is the O(n²) baseline: for each of ~2n centres, expand until mismatch. Manacher improves on it by reusing previous expansions.
Advantages of Manacher over EAC: - O(n) vs O(n²) time — crucial for large strings (>10⁴ characters). - Single pass produces all palindrome radii — can be reused for subqueries. - Elegant theoretical demonstration of linear string algorithms.
Disadvantages: - Higher constant factor: EAC is simpler and may be faster for small strings (<1000 characters). - Memory: O(n) extra space for P array (vs O(1) for EAC). - Complexity: harder to implement correctly; bugs in mirror logic are common. - Input must be transformed, adding overhead.
When to use Manacher: - When string length exceeds 10⁵ and you need optimal time. - When you need to answer multiple palindrome queries on the same string (e.g., count all palindromes).
When to use EAC: - Quick prototyping or interviews where O(n²) is acceptable. - When space is constrained. - When the string is very short.
Finding All Palindromic Substrings
Manacher computes all palindrome radii in one pass, so you can use P to answer queries like 'is s[l..r] a palindrome?' in O(1) after O(n) preprocessing.
Edge Cases and Common Pitfalls
Handling edge cases in Manacher is essential for production reliability. Here are the typical failures:
- Empty string: Should return empty string. Our function works because transform('') = '#', P[0]=0, max_len=0, start=0, s[0:0] = ''.
- Single character: transform('a') = '#a#', P[1]=1, returns 'a'. Works.
- All identical characters: Like 'aaaa'. The algorithm must handle large radii. The while loop may expand many times but total O(n). Ensure recursion depth is not an issue (iterative is fine).
- Very long string with no palindrome longer than 1: The algorithm will still run O(n) but each expansion step may be short. No issue.
- Characters that could be the separator: If input contains '#', you must choose another separator or escape. The safest approach is to use a character that is not present, e.g., chr(0) for ASCII strings.
Practice Problems
Sharpen your Manacher skills with these problems:
- [LeetCode 5 – Longest Palindromic Substring](https://leetcode.com/problems/longest-palindromic-substring/) – Classic problem; implement Manacher and compare with expand-around-centre.
- [LeetCode 647 – Palindromic Substrings](https://leetcode.com/problems/palindromic-substrings/) – Count all palindromic substrings; Manacher gives O(n) solution.
- [LeetCode 131 – Palindrome Partitioning](https://leetcode.com/problems/palindrome-partitioning/) – Partition string into palindromic substrings; use Manacher to precompute palindrome status for O(n²) DP.
- [Codeforces 245H – Queries for Number of Palindromes](https://codeforces.com/problemset/problem/245/H) – Many queries on substring palindromicity; precompute P array.
- [Codeforces 159D – Palindrome pairs](https://codeforces.com/problemset/problem/159/D) – Count pairs of palindromic substrings; Manacher helps compute palindrome endpoints.
- [SPOJ PALIN – The Next Palindrome](https://www.spoj.com/problems/PALIN/) – Not directly Manacher, but good practice for palindrome logic.
_Tip: For problems requiring multiple queries, precompute palindrome radii with Manacher and reuse the P array._
False Longest Palindrome in DNA Sequence Analysis
if i + P[i] > r but forgot to update centre 'c' when the new palindrome extended beyond r, causing subsequent mirror calculations to use a stale centre.- Always test linear string algorithms with random strings and validate against O(n^2) for small n.
- Edge cases with even-length palindromes and strings of repeated characters trigger boundary bugs.
- The centre must be updated to the new palindrome's centre when r advances.
Key takeaways
Common mistakes to avoid
3 patternsForgetting to update the centre c when r advances
Incorrect initialisation of P[i] when i >= r
Off-by-one in start index calculation
Interview Questions on This Topic
Why do we insert separator characters in Manacher's algorithm?
Frequently Asked Questions
That's Strings. Mark it forged?
6 min read · try the examples if you haven't