Problem
Given a string, find the longest contiguous substring that reads the same forwards and backwards. Unlike a palindromic subsequence, characters must be consecutive โ you cannot skip positions.
Example:
- Input:
s = "babad" - Output:
"bab"(or"aba"โ both are valid) - Explanation: Both "bab" and "aba" are length-3 palindromes; the full string "babad" is not a palindrome.
Counter-example: s = "cbbd" โ "b" and "bb" are palindromes, "cbbd" is not; the answer is "bb" with length 2, not "cbd" (which skips a character and is not a substring anyway).
Intuition
Every palindrome has a center โ either a single character (odd length, e.g., "aba") or two adjacent matching characters (even length, e.g., "abba"). Rather than checking all O(nยฒ) substrings and verifying each one, we can try expanding outward from each of the 2nโ1 possible centers. As soon as the characters on both sides stop matching, we stop. This runs in O(nยฒ) time but uses only O(1) extra space, and it's fast in practice because most expansions terminate quickly.
Solution โ Expand Around Center
For each possible center in the string, grow a window outward in both directions as long as the characters match. Track the start and length of the longest palindrome seen across all centers.
Steps:
- Initialize
best_start = 0andbest_length = 1to handle the single-character base case. - For each index
i, call the helper twice: once with(i, i)for odd-length palindromes and once with(i, i+1)for even-length ones. - In the helper, advance
leftleft andrightright while both pointers are in bounds and the characters match. - After the loop exits,
leftandrightare both one step past the palindrome boundary โ the actual palindrome iss[left+1 : right], with lengthright - left - 1. - Update
best_startandbest_lengthif this palindrome is strictly longer than the current best. - Return
s[best_start : best_start + best_length].
1def longestPalindrome(s: str) -> str:
2 best_start, best_length = 0, 1
3
4 def expand(left: int, right: int) -> None:
5 nonlocal best_start, best_length
6 while left >= 0 and right < len(s) and s[left] == s[right]:
7 left -= 1
8 right += 1
9 # After the loop, s[left] != s[right] or out of bounds;
10 # the palindrome spans [left+1, right-1], length = right - left - 1
11 length = right - left - 1
12 if length > best_length:
13 best_length = length
14 best_start = left + 1
15
16 for i in range(len(s)):
17 expand(i, i) # odd-length: single character center
18 expand(i, i + 1) # even-length: gap between characters
19
20 return s[best_start : best_start + best_length]Time: O(nยฒ) โ there are 2nโ1 centers, and each expansion visits at most O(n) characters in the worst case. Space: O(1) โ only a constant number of integer variables; the result is a view into the original string, not a copy until returned.
Complexity Summary
| Approach | Time | Space | When to use |
|---|---|---|---|
| Expand Around Center | O(nยฒ) | O(1) | Standard interview answer โ clean, fast in practice, and easy to explain and verify. |
Common Mistakes
- Skipping the even-length case: Only calling
expand(i, i)misses palindromes like "abba" โ even-length palindromes have no single center character. You must also callexpand(i, i+1)for every index. - Off-by-one when reading the palindrome boundary: After the loop,
leftandrighthave both gone one step past the edges of the palindrome. The palindrome iss[left+1 : right], nots[left : right+1]. Getting this backwards gives a two-character-too-wide string. - Wrong length formula: The palindrome length is
right - left - 1, notright - left + 1. Sinceleftis one before the start andrightis one after the end, the span is(right - 1) - (left + 1) + 1 = right - left - 1. - Using
>=instead of>when updating the best: Using>=causes the answer to shift to the last tied palindrome found rather than the first. The problem accepts any valid answer, but being consistent about strict>avoids subtle bugs in problems where the position matters. - Not initializing for the single-character base case: If the input has no palindrome longer than one character (e.g.,
"abcd"), the expansion loops all exit immediately. Initializingbest_length = 1andbest_start = 0returnss[0]correctly without any special-case code.
Related Problems
palindromic-substringsโ count every palindromic substring using the same expand-around-center technique, just incrementing a counter instead of tracking the longestlongest-palindromic-subsequenceโ characters no longer need to be contiguous; the palindrome can skip positions, which forces a DP approach insteadpalindrome-partitioningโ split a string into all-palindrome parts; palindrome detection via expansion is a natural subproblemvalid-palindromeโ the fundamental two-pointer palindrome check that underlies the expansion step herepalindrome-linked-listโ palindrome verification on a linked list using the two-pointer meeting-in-the-middle technique