Skip to content
ΣDSA Patterns
Menu
Language

KMP / String Matching

Guide 4 of 6 · Path 4 of 6

Interactive

Mental model

A worked animation for this problem. Scrub steps or press space to pause; re-tell the invariant out loud.

Step 1 of 6
a
a
b

add only in front

Make a palindrome by prepending the fewest characters. s = aab.

Demo preview: These solutions pass the automated test suite but have not been human-reviewed. Treat trade-offs and prose as draft.

Shortest Palindrome

Problem (restated)

Add the shortest prefix to s so the result is a palindrome. You may only add characters in front.

Intuition

You want the longest prefix of s that is already a palindrome, then prepend the reverse of the leftover suffix. That longest palindromic prefix is lps[-1] of s + '#' + reverse(s): the # blocks wrap-around, so the LPS at the end is how much of s matches a suffix of the reverse — a palindromic prefix.

Approaches

LPS of s + # + reverse(s)

Unverified
Time O(n)Space O(n)

Idea. comb = s + '#' + reverse(s). Build LPS. Prepend reverse(s)[0 : n - lps[-1]] to s.

Walkthrough. aacecaaa → longest palindromic prefix aacecaa, leftover a, prepend a → aaacecaaa.

Trade-offs. The # is required; without it the LPS can jump across the join. Naive expand-around-center from the start is O(n²).

Solution
function buildLps(pattern: string): number[] {
  const lps = new Array<number>(pattern.length).fill(0);
  let length = 0;
  for (let i = 1; i < pattern.length; i++) {
    while (length > 0 && pattern[i] !== pattern[length]) length = lps[length - 1]!;
    if (pattern[i] === pattern[length]) length++;
    lps[i] = length;
  }
  return lps;
}

export function shortestPalindrome(s: string): string {
  if (!s) return s;
  const rev = [...s].reverse().join("");
  const lps = buildLps(`${s}#${rev}`);
  return rev.slice(0, s.length - lps[lps.length - 1]!) + s;
}
function buildLps(pattern: string): number[] {
  const lps = new Array<number>(pattern.length).fill(0);
  let length = 0;
  for (let i = 1; i < pattern.length; i++) {
    while (length > 0 && pattern[i] !== pattern[length]) length = lps[length - 1]!;
    if (pattern[i] === pattern[length]) length++;
    lps[i] = length;
  }
  return lps;
}

export function shortestPalindrome(s: string): string {
  if (!s) return s;
  const rev = [...s].reverse().join("");
  const lps = buildLps(`${s}#${rev}`);
  return rev.slice(0, s.length - lps[lps.length - 1]!) + s;
}

Template connection

Shortest palindrome via LPS of pattern + reverse. Same LPS builder as LC 28.

Reflection