What Is a Palindrome?
A palindrome reads the same forward and backward.
| "racecar" | palindrome |
| "madam" | palindrome |
| "A man a plan a canal Panama" | palindrome (ignoring spaces/case) |
| "hello" | not a palindrome |
2. Valid Palindrome (Ignore Non-Alphanumeric)
def is_valid_palindrome(s):
"""
Check palindrome ignoring spaces, punctuation, case.
"A man, a plan, a canal: Panama" → True
"""
left, right = 0, len(s) - 1
while left < right:
while left < right and not s[left].isalnum():
left += 1
while left < right and not s[right].isalnum():
right -= 1
if s[left].lower() != s[right].lower():
return False
left += 1
right -= 1
return True
print(is_valid_palindrome("A man, a plan, a canal: Panama")) # True
print(is_valid_palindrome("race a car")) # False
3. Longest Palindromic Substring — Expand Around Center
def longest_palindrome_expand(s):
"""
Expand around each center (char or gap between chars).
O(n²) time, O(1) space.
"""
if not s:
return ""
start = end = 0
def expand(left, right):
while left >= 0 and right < len(s) and s[left] == s[right]:
left -= 1
right += 1
return left + 1, right - 1 # Return valid palindrome bounds
for i in range(len(s)):
# Odd length palindromes (center at i)
l1, r1 = expand(i, i)
# Even length palindromes (center between i and i+1)
l2, r2 = expand(i, i+1)
if r1 - l1 > end - start:
start, end = l1, r1
if r2 - l2 > end - start:
start, end = l2, r2
return s[start:end+1]
print(longest_palindrome_expand("babad")) # "bab" or "aba"
print(longest_palindrome_expand("cbbd")) # "bb"
print(longest_palindrome_expand("racecar")) # "racecar"
4. Longest Palindromic Substring — Manacher's Algorithm: O(n)
def manacher(s):
"""
Manacher's algorithm — O(n) time, O(n) space.
Finds longest palindromic substring in linear time.
"""
# Transform: "abc" → "#a#b#c#" (handles even/odd uniformly)
t = '#' + '#'.join(s) + '#'
n = len(t)
p = [0] * n # p[i] = radius of palindrome centered at i
center = right = 0 # Rightmost palindrome's center and right boundary
for i in range(n):
mirror = 2 * center - i # Mirror of i around center
if i < right:
p[i] = min(right - i, p[mirror]) # Use mirror info
# Expand around i
while i + p[i] + 1 < n and i - p[i] - 1 >= 0 and t[i + p[i] + 1] == t[i - p[i] - 1]:
p[i] += 1
# Update center and right boundary
if i + p[i] > right:
center, right = i, i + p[i]
# Find max palindrome radius
max_len = max(p)
center_idx = p.index(max_len)
# Convert back to original string indices
start = (center_idx - max_len) // 2
return s[start : start + max_len]
print(manacher("babad")) # "bab"
print(manacher("cbbd")) # "bb"
print(manacher("racecar")) # "racecar"
5. Count All Palindromic Substrings
def count_palindromes(s):
"""Count all palindromic substrings. O(n²)."""
count = 0
def expand(l, r):
nonlocal count
while l >= 0 and r < len(s) and s[l] == s[r]:
count += 1
l -= 1
r += 1
for i in range(len(s)):
expand(i, i) # Odd length
expand(i, i+1) # Even length
return count
print(count_palindromes("abc")) # 3 — "a", "b", "c"
print(count_palindromes("aaa")) # 6 — "a","a","a","aa","aa","aaa"
print(count_palindromes("abcba")) # 7
6. Palindrome Partitioning
def min_cuts_palindrome(s):
"""
Minimum cuts to partition s into palindromes.
DP approach. O(n²).
"""
n = len(s)
# is_pal[i][j] = True if s[i..j] is palindrome
is_pal = [[False]*n for _ in range(n)]
for i in range(n):
is_pal[i][i] = True
for i in range(n-1):
is_pal[i][i+1] = (s[i] == s[i+1])
for length in range(3, n+1):
for i in range(n - length + 1):
j = i + length - 1
is_pal[i][j] = (s[i] == s[j]) and is_pal[i+1][j-1]
# dp[i] = min cuts for s[0..i]
dp = list(range(n))
for i in range(1, n):
if is_pal[0][i]:
dp[i] = 0 # Entire s[0..i] is palindrome
else:
for j in range(1, i+1):
if is_pal[j][i]:
dp[i] = min(dp[i], dp[j-1] + 1)
return dp[n-1]
print(min_cuts_palindrome("aab")) # 1 — "a|ab" or "aa|b"
print(min_cuts_palindrome("racecar")) # 0 — whole string is palindrome
7. Palindrome Pairs
def palindrome_pairs(words):
"""
Find all pairs (i,j) where words[i]+words[j] is palindrome.
O(k² × n) brute force, O(n × k²) with hash map.
"""
def is_pal(s): return s == s[::-1]
lookup = {word: i for i, word in enumerate(words)}
result = []
for i, word in enumerate(words):
for k in range(len(word) + 1):
prefix = word[:k]
suffix = word[k:]
# If suffix reversed is in dict and prefix is palindrome
rev_suffix = suffix[::-1]
if is_pal(prefix) and rev_suffix in lookup and lookup[rev_suffix] != i:
result.append([lookup[rev_suffix], i])
# If prefix reversed is in dict and suffix is palindrome (avoid duplicate for k=0)
if k != len(word) and is_pal(suffix):
rev_prefix = prefix[::-1]
if rev_prefix in lookup and lookup[rev_prefix] != i:
result.append([i, lookup[rev_prefix]])
return result
print(palindrome_pairs(["abcd","dcba","lls","s","sssll"]))
# [[0,1],[1,0],[3,2],[2,4]]
Palindrome Pattern Summary
| Problem | Technique | Time | Space |
|---|
| Check palindrome | Two pointers | O(n) | O(1) |
| Longest palindrome | Expand around center | O(n²) | O(1) |
| Longest palindrome (optimal) | Manacher's | O(n) | O(n) |
| Count all palindromes | Expand around center | O(n²) | O(1) |
| Min cuts partition | DP (is_pal + dp) | O(n²) | O(n²) |
Key insight: For palindromes, the "expand around center" technique is the most intuitive and covers both odd and even length palindromes in a clean two-pass loop.
*Next: Anagram Problems*
Exam Focus
Revise definitions, diagrams, examples, and short-answer points for Palindrome Problems — Check, Longest, Count, and Expand Around Center.
Interview Use
Prepare one clear explanation, one practical example, and one common mistake for this Data Structures & Algorithms topic.
Search Terms
data-structures-algorithms, data structures & algorithms, data, structures, algorithms, strings, palindrome, problems