Palindromic Substrings
Problem
s are palindromes. Substrings
are counted by position, so equal text at different start/end indices counts more than
once.s = "abc"Output3 (a, b, c)s = "aaa"Output6 (a, a, a, aa, aa, aaa)1 ≤ s.length ≤ 1000 · lowercase English letters
Try it yourself
class Solution {
public int countSubstrings(String s) {
}
}
2n-1 centres, every successful step is
one new palindrome — so just count the steps.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 ForceTest Every Substring
O(n3)Space O(1)Enumerate all n(n+1)/2 index pairs (i, j) and check each
window with two pointers moving inward. Add one for every palindrome.
public int countSubstrings(String s) { int n = s.length(), count = 0; for (int i = 0; i < n; i++) { for (int j = i; j < n; j++) { if (isPalindrome(s, i, j)) count++; } } return count; } private boolean isPalindrome(String s, int l, int r) { while (l < r) { if (s.charAt(l++) != s.charAt(r--)) return false; } return true; }
Straightforward, but re-checks the same inner characters over and over.
Better2-D Palindrome Table
O(n2)Space O(n2)s[i..j] is a palindrome iff its ends match and the middle
s[i+1..j-1] is one (or is shorter than 2). Filling rows from the bottom up
makes the middle answer available in O(1), so each pair costs constant time.
public int countSubstrings(String s) { int n = s.length(), count = 0; boolean[][] pal = new boolean[n][n]; for (int i = n - 1; i >= 0; i--) { for (int j = i; j < n; j++) { if (s.charAt(i) == s.charAt(j) && (j - i < 3 || pal[i + 1][j - 1])) { pal[i][j] = true; count++; } } } return count; }
Removes the cubic re-checking at the price of an n×n table.
OptimalExpand Around Every Centre
O(n2)Space O(1)For each index run two expansions, (c, c) and (c, c+1).
While the ends match, the current window is a palindrome — count it and widen by
one on each side. Stop at the first mismatch, because every wider window around that
centre would contain it.
public int countSubstrings(String s) { int count = 0; for (int c = 0; c < s.length(); c++) { count += expand(s, c, c); // odd-length palindromes count += expand(s, c, c + 1); // even-length palindromes } return count; } // Number of palindromes centred at (l, r). private int expand(String s, int l, int r) { int found = 0; while (l >= 0 && r < s.length() && s.charAt(l) == s.charAt(r)) { found++; l--; r++; } return found; }
Dry runs = "abc"
- c=0 oddal raa"a" → count=1
- c=0 evenalara"aa" → count=2
- c=1 oddalaara, aaa → count=4
- c=1 evenaalar"aa" → count=5
- c=2 oddaaal r"a" → 6 ✔
Quadratic time, constant memory and tiny code — the standard answer.
(radius+1)/2 over
its radius array — a nice follow-up mention.At a glance
| Level | Approach | Time | Space |
|---|---|---|---|
| Brute Force | Test Every Substring | O(n3) | O(1) |
| Better | 2-D Palindrome Table | O(n2) | O(n2) |
| Optimal | Expand Around Every Centre | O(n2) | O(1) |
- Counting distinct palindrome strings instead of positions —
"aaa"is 6, not 3. - Dropping even-length centres — undercounts strings like
"aa". - Filling the table top-down by
i—pal[i+1][j-1]is not ready yet.
- Ask whether duplicates by position count — it decides the whole approach.
- Reuse the centre-expansion helper from Longest Palindromic Substring and say so.
- State the bound: at most n(n+1)/2 palindromes, so output-driven O(n2) is natural.