Longest Palindromic Substring
Problem
s, return its longest contiguous piece that reads the
same forwards and backwards. If several pieces tie for the maximum length, any one of them is
accepted.s = "babad"Output"bab" ("aba" is also fine)s = "cbbd"Output"bb"s = "x"Output"x"1 ≤ s.length ≤ 1000 · s holds only digits and English letters
Try it yourself
class Solution {
public String longestPalindrome(String s) {
}
}
2n-1 centres, and growing
outwards from each one finds every palindrome without any table.Instinct Two strings compared char by char (match, edit, interleave) means dp[i][j] over prefixes; one string's palindromes or splits means dp[l][r] or dp[i].
Memory hack Match? Go diagonal. Mismatch? Ask the neighbours. Up drops a char from s, left drops one from t, diagonal uses both.
Open the String DP guide →Approaches
Brute ForceCheck Every Substring, Longest First
O(n3)Space O(1)Try window lengths from n down to 1 and, for each start, test the
window with two pointers. The first palindrome found is guaranteed to be a longest
one.
public String longestPalindrome(String s) { int n = s.length(); for (int len = n; len >= 1; len--) { for (int i = 0; i + len <= n; i++) { if (isPalindrome(s, i, i + len - 1)) return s.substring(i, i + len); } } return ""; } private boolean isPalindrome(String s, int l, int r) { while (l < r) { if (s.charAt(l++) != s.charAt(r--)) return false; } return true; }
Obviously correct, but the inner palindrome check makes it cubic.
Better2-D Palindrome Table
O(n2)Space O(n2)pal[i][j] is true when the ends match and the inside
s[i+1..j-1] is a palindrome (or has length ≤ 1). Filling i
from right to left guarantees the inner cell is ready, so each check is O(1).
public String longestPalindrome(String s) { int n = s.length(); boolean[][] pal = new boolean[n][n]; int start = 0, best = 1; for (int i = n - 1; i >= 0; i--) { for (int j = i; j < n; j++) { // ends match and the middle (length <= 1, or pal[i+1][j-1]) is a palindrome if (s.charAt(i) == s.charAt(j) && (j - i < 3 || pal[i + 1][j - 1])) { pal[i][j] = true; if (j - i + 1 > best) { best = j - i + 1; start = i; } } } } return s.substring(start, start + best); }
Quadratic time, but a million-cell boolean grid for n = 1000.
OptimalExpand Around Every Centre
O(n2)Space O(1)For each index c expand twice: from (c, c) for odd
lengths and from (c, c+1) for even ones, stopping at the first mismatch.
The table's dependency chain is exactly this outward walk, so we can drop the table
and keep only the best window.
public String longestPalindrome(String s) { int start = 0, end = 0; // best window is s[start..end] for (int c = 0; c < s.length(); c++) { int len = Math.max(expand(s, c, c), expand(s, c, c + 1)); if (len > end - start + 1) { start = c - (len - 1) / 2; end = c + len / 2; } } return s.substring(start, end + 1); } // Length of the widest palindrome grown outward from (l, r). private int expand(String s, int l, int r) { while (l >= 0 && r < s.length() && s.charAt(l) == s.charAt(r)) { l--; r++; } return r - l - 1; }
Dry runs = "babad"
- c=0bcabad"b", best=1
- c=1blacbradb==b → "bab", best=3
- c=2balbcard"aba" ties, keep
- c=3bablacdrb≠d stop
- c=4babadcanswer "bab" ✔
Same time as the table, constant memory and short code — the expected answer.
At a glance
| Level | Approach | Time | Space |
|---|---|---|---|
| Brute Force | Check Every Substring, Longest First | O(n3) | O(1) |
| Better | 2-D Palindrome Table | O(n2) | O(n2) |
| Optimal | Expand Around Every Centre | O(n2) | O(1) |
- Forgetting even-length centres — misses answers like
"bb". - Wrong start index from a centre: use
c - (len-1)/2, which works for odd and even. - Confusing substring with subsequence — that is a different (LPS 516) problem.
- Clarify that any one of several tied answers is acceptable.
- Explain why there are
2n-1centres, then code the sharedexpandhelper. - Note the table's O(n2) memory as the reason to prefer centre expansion.