Browse curriculum

Shortest Palindrome

Turn a string into the shortest palindrome by adding characters only in front, using the KMP prefix function to find the longest prefix that is already a palindrome.

Problem Understanding

Shortest Palindrome (LeetCode 214): given s, add characters only in front of it to make the shortest possible palindrome, and return that palindrome.

Examples: s = "aacecaaa" → "aaacecaaa"; s = "abcd" → "dcbabcd".

The Intuition: Keep the Palindrome Already There

The fewest characters to add means the longest part of s left alone:

  1. Find the longest prefix of s that is a palindrome.
  2. Mirror the rest of s and put it in front.
  3. Find that prefix in O(n) by running KMP's prefix function on s + '#' + reverse(s).

The brute-force check of a single string is Valid Palindrome, and the same fast string matching appears in Find the Index of the First Occurrence in a String.

Interactive Walkthrough

t = s + # + REVERSE(s) · 17 CHARACTERS

BUILD t

t

a

0

a

1

c

2

e

3

c

4

a

5

a

6

a

7

#

8

a

9

a

10

a

11

c

12

e

13

c

14

a

15

a

16

prefix under i

lps

0

i

:

j

:

answer

:

 

t = s + "#" + reverse(s) = "aacecaaa#aaacecaa". lps[0] = 0

Next

t[1] = "a" = t[0]: the match grows, lps[1] = 1

Free preview: the first 7 of 23 steps

s

Watch the lps row fill under t: i walks t, j marks the prefix character it is matched against, and mismatches fall back. Try Fall back to see j jump back to a shorter prefix and still find a match.

Dry Run Table

Input: s = "aacecaaa", t = "aacecaaa#aaacecaa" (17 characters).

i t[i] lps[i] How
1 a 1 matches t[0]
2–4 c, e, c 0 fall back to 0, no match
5, 6 a, a 1, 2 match
7 a 2 fall back to 1, then match
8 # 0 falls back to 0
9–16 a a a c e c a a 1, 2, 2, 3, 4, 5, 6, 7 match (one fall-back at i = 11)

lps[16] = 7: "aacecaa" is the longest palindromic prefix, so add "a" in front: "aaacecaaa".

The Solution Template

KMP prefix function on s + '#' + reverse(s).

Shortest Palindrome Code
1var shortestPalindrome = function(s) {
2 const rev = [...s].reverse().join('');
3 const t = s + '#' + rev;
4 const lps = new Array(t.length).fill(0);
5 for (let i = 1; i < t.length; i++) {
6 let j = lps[i - 1];
7 while (j > 0 && t[i] !== t[j]) j = lps[j - 1];
8 if (t[i] === t[j]) j++;
9 lps[i] = j;
10 }
11 const k = lps[t.length - 1];
12 return rev.slice(0, s.length - k) + s;
13};

Edge Cases & Common Mistakes

  • Empty string: the answer is empty.
  • Already a palindrome: lps ends at n and nothing is added.
  • Forgetting the separator: for s = "aaa", t = "aaaaaa" would give lps 5, longer than s.
  • Adding characters at the end: the problem only allows the front.
  • O(n²) prefix checks: correct but too slow for 5 · 10^4 characters of all "a"s with one "b".

Summary

Compute KMP's lps for s + '#' + reverse(s); its last value k is the longest palindromic prefix of s. Return reverse(s[k:]) + s. O(n) time and space.

More practice: DSA interview questions with answers · patterns cheat sheet

The approach, step by step

  1. Build t

    t = s + '#' + reverse(s).

  2. Compute lps

    KMP prefix function: for each i, fall back with j = lps[j − 1] on mismatches, extend on a match.

  3. Assemble

    k = lps of the last character; return reverse(s[k:]) + s.

Frequently asked questions

Why does the answer depend on the longest palindromic prefix?

Characters can only go in front, so s itself ends the result. The result is a palindrome exactly when its start mirrors s's end. Whatever prefix of s is already a palindrome can sit in the middle untouched; everything after it must be mirrored in front. Keeping the longest such prefix adds the fewest characters.


Why build s + '#' + reverse(s)?

A prefix of s is a palindrome when it equals the matching suffix of reverse(s). KMP's lps value at the end of t is the longest prefix of t that is also a suffix of t, which is exactly the longest prefix of s matching a suffix of reverse(s). The '#' cannot match any letter, so the match can never be longer than s.


What happens on a mismatch?

Instead of restarting, j falls back to lps[j − 1], the next shorter prefix that is still a suffix of what was matched. Each character can only push j up by one, so the total falling back is bounded by the length of t and the whole pass is O(n).


Is there a simpler approach?

Checking each prefix from longest to shortest for being a palindrome is O(n²) and is fine for short strings. Rolling hashes (Rabin-Karp) or Manacher's algorithm also give O(n), but the KMP version is the one most often expected.

New to the technique behind this problem? Watch it run step by step:

Two Pointers Visualization

Applying for software engineering roles? Free, no sign-up:

AI resume checker: match your resume to the job description