Menu
DSA interview questionsQuestion 100 of 147

DSA interview question · Question 100 of 147

Palindromic Substrings: Count Every Palindrome by Expanding Around Centres

  • Medium
  • coding
  • ~15 min
  • Medium relevance
  • 4 min read
  • Updated Oct 2026

Short answer

Each palindromic substring has exactly one centre, either a character or the gap between two characters. For each of the 2n - 1 centres, expand outwards while both ends match and add one for every successful step. That counts each palindrome once in O(n^2) time and O(1) space. The interval DP pal[i][j] = (s[i] == s[j]) and pal[i + 1][j - 1] gives the same count in O(n^2) time and space.

On this page
  1. Problem
  2. Examples
  3. Approach 1: check every substring
  4. Approach 2: interval DP
  5. State, recurrence and base cases
  6. Filled table for “ooo”
  7. Bottom-up
  8. Memoised
  9. Approach 3: optimal in space, expand around centres
  10. Complexity
  11. Tests
  12. Edge cases and pitfalls
  13. Where this shows up in data engineering

Problem

Given a string, count how many of its substrings are palindromes. Substrings at different positions count separately, even if they contain the same characters.

This is widely known as LeetCode 647, “Palindromic Substrings”. It uses the same tools as Longest Palindromic Substring.

Assume up to 1,000 characters.

Examples

"xyz"   -> 3   (x, y, z)
"ooo"   -> 6   (o, o, o, oo, oo, ooo)
"refer" -> 7   (r, e, f, e, r, efe, refer)
""      -> 0

Approach 1: check every substring

def count_substrings_brute(s):
    count = 0
    for i in range(len(s)):
        for j in range(i, len(s)):
            piece = s[i:j + 1]
            if piece == piece[::-1]:
                count += 1
    return count

O(n^3): n^2 substrings, O(n) to check each.

Approach 2: interval DP

State, recurrence and base cases

  • State: pal[i][j] = whether s[i..j] is a palindrome.
  • Recurrence: pal[i][j] = s[i] == s[j] and (j - i < 2 or pal[i + 1][j - 1]).
  • Base cases: covered by j - i < 2: single characters, and pairs of equal characters.
  • Answer: the number of true cells.

Filled table for “ooo”

i \ j 0 1 2
0 T T T
1 T T
2 T

Six true cells, so 6.

Bottom-up

def count_substrings_dp(s):
    size = len(s)
    pal = [[False] * size for _ in range(size)]
    count = 0
    for i in range(size - 1, -1, -1):
        for j in range(i, size):
            if s[i] == s[j] and (j - i < 2 or pal[i + 1][j - 1]):
                pal[i][j] = True
                count += 1
    return count

Memoised

from functools import lru_cache

def count_substrings_memo(s):
    @lru_cache(maxsize=None)
    def is_pal(i, j):
        if i >= j:
            return True
        return s[i] == s[j] and is_pal(i + 1, j - 1)
    return sum(is_pal(i, j) for i in range(len(s)) for j in range(i, len(s)))

Approach 3: optimal in space, expand around centres

def count_substrings(s):
    count = 0
    for centre in range(2 * len(s) - 1):
        lo = centre // 2
        hi = lo + centre % 2          # even centre index: a character; odd: the gap after it
        while lo >= 0 and hi < len(s) and s[lo] == s[hi]:
            count += 1
            lo -= 1
            hi += 1
    return count

Iterating over 2n - 1 centres with one loop handles both odd and even lengths without duplicate code.

Complexity

Method Time Space
Brute force O(n^3) O(n) for the slice
Interval DP O(n^2) O(n^2)
Expand around centres O(n^2) O(1)

Tests

for fn in (count_substrings, count_substrings_dp, count_substrings_memo, count_substrings_brute):
    assert fn("xyz") == 3
    assert fn("ooo") == 6
    assert fn("refer") == 7
    assert fn("") == 0                           # empty
    assert fn("q") == 1                          # single character
    assert fn("abab") == 6                       # a, b, a, b, aba, bab

# All-equal string of length k has k * (k + 1) / 2 palindromic substrings
assert count_substrings("z" * 50) == 50 * 51 // 2

import random
random.seed(10)
for _ in range(100):
    s = "".join(random.choice("abc") for _ in range(random.randint(0, 10)))
    assert count_substrings(s) == count_substrings_brute(s) == count_substrings_dp(s)

Edge cases and pitfalls

  • Forgetting even centres undercounts strings like "oo".
  • Counting distinct palindromes by mistake. "ooo" has 6 palindromic substrings but only 3 distinct ones.
  • DP fill order, as in every interval DP: inner intervals first.
  • Worst case is a string of one repeated letter, where every substring is a palindrome; your solution should still be O(n^2), not worse.

Where this shows up in data engineering

There is no direct pipeline use. The centre-expansion idea, growing a window outward while a condition holds, is a useful cousin of the sliding-window techniques used on event streams.

By Data Career Hub Editorial · Last reviewed Oct 2026 · Python 3 solutions verified with assert-based tests

Progress is saved in this browser only. No account needed.

Search
Filter by type