Problem Statement
Palindrome Partitioning
You are given a string s. A palindrome is a word that reads the same forwards and backwards, like "aba" or "aa". Your job is to cut the string into pieces so that every single piece is a palindrome, and to find every possible way to do that. The tool we reach for here is backtracking. Backtracking means: try a choice, go deeper to see where it leads, and if it does not work out, undo the choice and try the next one. Picture exploring a maze where you mark your path with chalk, and when you hit a dead end you walk back, erase the last mark, and try a different turn. At each spot in the string we try every possible piece that can start there. If that piece is a palindrome, we keep it and solve the rest of the string the same way. When we have used up the whole string, we have one complete answer and we save it.
Signals to notice
Brute force first
Without precomputation, each palindrome check is O(n) — total O(n × 2^n).
The key insight
Precompute isPalin[i][j]. Backtracking: at each position, try all cuts where the prefix is a palindrome. Recurse on the rest. O(n × 2^n) but with O(1) palindrome checks.
Trace it on s = "aab"
backtrack(0,[]): end=0 -> "a" palindrome, push -> path=["a"], recurse backtrack(1) backtrack(1,["a"]): end=1 -> "a" palindrome, push -> path=["a","a"], recurse backtrack(2) backtrack(2,["a","a"]): end=2 -> "b" palindrome, push -> path=["a","a","b"], recurse backtrack(3) backtrack(3): start==len(s) -> record ["a","a","b"]; unwind/pop back to backtrack(1) backtrack(1) end=2 -> "ab" not palindrome, skip; pop -> back to backtrack(0) backtrack(0): end=1 -> "aa" palindrome, push -> path=["aa"], recurse backtrack(2) backtrack(2,["aa"]): end=2 -> "b" palindrome -> path=["aa","b"], backtrack(3) records ["aa","b"] backtrack(0): end=2 -> "aab" not palindrome, skip; return result=[["a","a","b"],["aa","b"]]
What must stay true
At each position, only palindromic prefixes are valid cuts. The precomputed table makes each check O(1), keeping the backtracking efficient.
Shape of the loop
function backtrack(start, path):
if start == len(s): record copy of path; return
for end in start..len(s)-1:
if isPalindrome(start, end):
path.push(s[start..end]); backtrack(end+1, path); path.pop()Pseudocode only — the full worked solution lives in the Solution tab.
Easy way to go wrong
Not precomputing palindromes — inline checking adds O(n) per check and slows the solution significantly.