MediumDynamic Programming

Palindromic Substrings โ€” Solution

Problem

Given a string, count how many of its substrings are palindromes โ€” sequences that read the same forwards and backwards. Every individual character counts as a palindrome on its own.

  • Input: s = "aaa"
  • Output: 6
  • Explanation: The palindromic substrings are "a", "a", "a", "aa", "aa", and "aaa".

Counter-example: "abc" produces 3 โ€” only the three single characters, since no multi-character substring reads the same in both directions.

Intuition

Every palindrome has a center: either a single character (odd length) or the gap between two adjacent characters (even length). If you stand at a center and expand outward one character at a time, every step where the left and right characters match reveals another palindrome. The key insight is that all valid expansions from a center form a chain โ€” once a mismatch occurs, no further expansion from that center can be palindromic.

Approach 1 โ€” Expand Around Center

For each of the 2n โˆ’ 1 possible centers, expand outward while the flanking characters match. Each successful expansion contributes one palindrome to the count.

  1. Iterate over every index as a potential center.
  2. For odd-length palindromes, expand starting with both pointers at the same index.
  3. For even-length palindromes, expand starting with the left pointer at the index and the right pointer one ahead.
  4. In each expansion, advance inward-out while bounds hold and characters match, incrementing the count each step.
  5. Return the total count.
1def countSubstrings(s: str) -> int:
2    count = 0
3
4    def expand_from_center(left: int, right: int) -> int:
5        palindromes = 0
6        while left >= 0 and right < len(s) and s[left] == s[right]:
7            palindromes += 1
8            left -= 1
9            right += 1
10        return palindromes
11
12    for center in range(len(s)):
13        count += expand_from_center(center, center)      # odd-length: single char center
14        count += expand_from_center(center, center + 1)  # even-length: gap between chars
15    return count

Time: O(nยฒ) โ€” at most n expansions per center, across 2n โˆ’ 1 centers.
Space: O(1) โ€” only two pointer variables per expansion; no auxiliary storage.

Approach 2 โ€” Dynamic Programming

Build a 2-D boolean table where dp[start][end] is true when s[start..end] is a palindrome. Fill it by increasing substring length so that shorter results are always available when computing longer ones.

  1. Create an n ร— n boolean table, initially all false.
  2. Iterate length from 1 to n.
  3. For each starting index, compute the ending index.
  4. Mark dp[start][end] true when the outer characters match and the inner substring (already computed) is also a palindrome. Length 1 and 2 need no inner lookup.
  5. Increment the count whenever a cell is marked true.
1def countSubstrings(s: str) -> int:
2    n = len(s)
3    count = 0
4    dp = [[False] * n for _ in range(n)]
5
6    for length in range(1, n + 1):
7        for start in range(n - length + 1):
8            end = start + length - 1
9            # length <= 2: no inner substring to check, outer match is sufficient
10            if s[start] == s[end] and (length <= 2 or dp[start + 1][end - 1]):
11                dp[start][end] = True
12                count += 1
13    return count

Time: O(nยฒ) โ€” filling every cell in the n ร— n table once.
Space: O(nยฒ) โ€” the DP table stores a boolean for every substring.

Complexity Summary

ApproachTimeSpaceWhen to use
Expand Around CenterO(nยฒ)O(1)Default choice โ€” minimal overhead, easy to implement
Dynamic ProgrammingO(nยฒ)O(nยฒ)When you need the full palindrome table for follow-up queries (e.g., reusing it in a partitioning problem)

Common Mistakes

  • Forgetting even-length palindromes โ€” expanding only from a single center character finds odd-length palindromes like "aba" but misses "aa". A second expansion starting at (center, center + 1) is required.
  • Checking characters before checking bounds โ€” s[left] == s[right] before left >= 0 && right < n causes an index-out-of-bounds when the expansion reaches the string edge. Bounds must come first in the condition.
  • Iterating DP by start index instead of by length โ€” if you loop over start in the outer loop, dp[start + 1][end - 1] references a cell for a longer interval that hasn't been computed yet. The outer loop must be over length.
  • Omitting the length โ‰ค 2 guard in DP โ€” for a length-2 substring s[i..i+1], dp[i+1][i] is out-of-order and undefined. The condition length <= 2 short-circuits this lookup, treating a matching pair of adjacent characters as a base case.
  • Returning a substring instead of a count โ€” the problem asks how many palindromic substrings exist, not which one is longest. A common slip is returning max_length or the actual substring, especially after recently solving Longest Palindromic Substring.

Related Problems

Ready to practice? Try it on SkillFlow

Adaptive problems, AI follow-up interviews, and a skill score that shows exactly where you need to improve.

Practice This Problem โ†’