Exercises — Palindrome algorithms — Manacher's algorithm
This page is a self-testing ladder. Each rung climbs one cognitive level, from recognising what the pieces mean up to mastering the amortized argument. Every problem has a full worked solution hidden inside a collapsible callout — try first, then reveal.
Prerequisites live in the parent note Manacher's algorithm and neighbours Expand Around Center, Longest Palindromic Substring, Amortized Analysis, Z-Algorithm, KMP failure function, and Palindromic Tree (Eertree).
Level 1 — Recognition
L1.1 — Build the transform
Problem. Write the transformed string for . What is its length ? Which formula gives from ?
Recall Solution
Insert # at every gap and both ends:
That string has 7 characters. In general . Here , so . ✓
Why ? There are real characters and gaps (before, between, after), giving separators-plus-reals.
L1.2 — Read a value
Problem. In (indices ), the center sits on the b. By hand, . What palindrome in the original does this describe, and how long is it?
Recall Solution
Radius around index spans indices , i.e. the whole #a#b#a#. Removing the spacers gives aba.
The recovery rule says the original palindrome length equals directly, so length . The substring is ==aba==. ✓
Level 2 — Application
L2.1 — Full trace on s = "aba"
Problem. Run Manacher on . Fill the table: for each , give the mirror (if ), the initial , the value after expansion, and any update.
Recall Solution
Start .
| init | after expand | update | |||
|---|---|---|---|---|---|
| 0 | # | — () | 0 | 0 | — |
| 1 | a | — () | 0 | 1 (t[0]=#=t[2]=#) |
|
| 2 | # | , | 0 | — | |
| 3 | b | — (, since ) | 0 | 3 (b, then #=#... a=a... #=#) |
|
| 4 | # | , | 0 | — | |
| 5 | a | , | 1 | — | |
| 6 | # | , | 0 | — |
Final . at → longest palindrome aba, length 3. ✓
Why no expansion at ? Mirror had , and (equals the wall). We take ; the value touches the wall so in principle we try to expand — but t[7] is out of bounds, so nothing changes. This is Case B degenerating harmlessly at the string edge.
L2.2 — Recover the substring index
Problem. For s = "abacabad", suppose the maximum radius is at -index . Give the starting index in and the substring length.
Recall Solution
The mapping rule: start = (k - P[k]) // 2.
Length . So the substring is s[1:6].
For s = "abacabad", s[1:6] = "bacab", which is a palindrome of length 5. ✓
Why the ? is twice as dense as (every real char has a # neighbour). A left edge at -index corresponds to an -index of half that — and because starts with #, integer division lands exactly.
Level 3 — Analysis
L3.1 — Counting all palindromic substrings
Problem. Using the fact that the number of palindromic substrings centered at is , count the total number of palindromic substrings of s = "aaa".
Recall Solution
, indices . Compute (you can verify by symmetry): ... wait, aaa fully palindromic. Let's run it:
- (
a): expands to , set . - (
#): mirror ; init ; expandt[1]=a=t[3]=a→ ; thent[0]=#=t[4]=#→ ; set . - (
a): mirror ; init ; expandt[2]=#=t[4]=#→ 2,t[1]=a=t[5]=a→ 3,t[0]=#=t[6]=#→ wait bounds:t[0]andt[6]→ 3; set . - (
#): mirror ; init ; can't expand past bounds → 2. - (
a): mirror ; init → 1. - (
#): 0.
So . Now sum :
The 6 palindromic substrings of aaa: three single a, two aa, one aaa → . ✓ Total = 6.
L3.2 — Why and not just the mirror
Problem. Give a concrete and index where blindly setting P[i] = P[2C-i] (no cap) would claim a palindrome that does not exist. Explain in one sentence why the cap fixes it.
Recall Solution
Take — no, simpler. Consider the classic from abacaba-style where a mirror palindrome pokes out. A minimal example:
(from s="baab"). Suppose (# at middle), , and we reach . Mirror . If but , that's Case A (fits) — not the poke-out case.
The poke-out happens when : the mirror palindrome extends left past the big palindrome's left edge . Beyond that edge there is no symmetry guarantee, so copying asserts matching characters we never verified.
One sentence: capping at trusts symmetry only up to the wall , then earns any extra by actually comparing characters. ✓
Level 4 — Synthesis
L4.1 — Amortized bound, from scratch
Problem. Prove that the total number of character comparisons in all expand loops across a full run is at most (the length of ). Reference the figure for the mechanism.

Recall Solution
Split each iteration's work into two buckets:
- The
minstep is : pure arithmetic, no comparisons. Ignore it. - The expand loop does one character comparison per iteration. Here is the key: after the initial
min, the palindrome at already reaches up to (the wall). Every successful comparison inside the expand loop pushes the palindrome's right edge to , and then we executeR = i + P[i].
Look at the figure: the red wall marker only ever slides right, never left. Each successful expand comparison advances by exactly one position (in ). Since starts at and can never exceed , the number of successful comparisons over the whole run is .
Each expand loop ends with at most one failing comparison (the mismatch that stops it), and there are centers, giving failing comparisons. Total comparisons . ✓ This is the Amortized Analysis argument: individual centers may expand a lot, but the shared resource can only move forward steps total.
L4.2 — Design a worst case for naive expand
Problem. Expand Around Center runs in on some inputs. Construct a family of strings of length that forces this, and compute the exact number of successful expand comparisons for () under the naive (no-mirror) algorithm.
Recall Solution
The worst case is the all-same string . Every center is the middle of a large palindrome, so expansion runs far from each center.
Count successful comparisons for aaaa on (indices ), where naive expand tries every center independently:
By symmetry the radii are (the middle # at centers the whole aaaa).
Sum of radii = number of successful comparisons under naive expand (each expansion is one success):
So naive does 16 successful comparisons for . In general → . Manacher, by contrast, keeps total expansion bounded by because it reuses mirror values. ✓
Level 5 — Mastery
L5.1 — Relate to Z-Algorithm reflection
Problem. Both Manacher and the Z-Algorithm maintain a rightmost window and reuse previously computed values via reflection. State the analogous recurrence in the Z-algorithm and name the one structural difference in what gets reflected.
Recall Solution
In the Z-algorithm we maintain a window = the rightmost segment matching a prefix. For position inside it, the mirror is (offset from the window's left start), and: Compare Manacher: , then expand. The structural difference: Manacher reflects across a center (mirror index , a point reflection), because palindromes are symmetric about a point. The Z-algorithm reflects to the window's start (translation to ), because prefix-matches are compared against a fixed prefix, not a mirror. Both are the same amortized idea: trust the reused value up to the wall , then earn the rest. Related failure-function machinery appears in KMP failure function and the palindrome-specific automaton in Palindromic Tree (Eertree). ✓
L5.2 — Longest palindromic substring, end to end
Problem. For s = "forgeeksskeegfor", apply the full pipeline: what is the longest palindromic substring and its length? Show which and produce it, and verify the start-index mapping.
Recall Solution
The obvious long palindrome is geeksskeeg (length 10). Let's confirm via the pipeline.
In , this palindrome is even-length in , so its center in is a #. Its radius in equals its original length, so at that center.
The palindrome geeksskeeg starts at -index 3 (f o r g..., g is index 3). Mapping: start = (k - P[k]) // 2. We need (k - 10)//2 = 3, so , i.e. . That -index is the # between the two s characters — the true even center. ✓
Final answer: longest palindromic substring = ==geeksskeeg, length 10==. This matches Longest Palindromic Substring via s[start:start+P[k]] = s[3:13]. ✓
Recall Self-check: five one-line reveals
P[i] equals what, in the original string? ::: The exact length of the palindrome centered at t[i] in s. Why min(P[2C-i], R-i) instead of just P[2C-i]? ::: Because symmetry is only guaranteed up to the wall R; past it we must verify. What single quantity makes the algorithm O(n)? ::: The wall R only moves rightward, at most m steps in total. Map t-index k with radius P[k] to an s start-index. ::: start = (k - P[k]) // 2. Why insert # separators? ::: They make every palindrome odd-length so only one case is handled.