3.8.9 · D4String Algorithms

Exercises — Palindrome algorithms — Manacher's algorithm

2,440 words11 min readBack to topic

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 ; expand t[1]=a=t[3]=a; then t[0]=#=t[4]=#; set .
  • (a): mirror ; init ; expand t[2]=#=t[4]=# → 2, t[1]=a=t[5]=a → 3, t[0]=#=t[6]=# → wait bounds: t[0] and t[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.

Figure — Palindrome algorithms — Manacher's algorithm
Recall Solution

Split each iteration's work into two buckets:

  1. The min step is : pure arithmetic, no comparisons. Ignore it.
  2. 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 execute R = 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.