2Brute2Best
🔥0

Palindromic Substrings

Medium·LeetCode #647Basic Anchor
LeetCode
30-Second Mental ModelPalindrome DP (dp[i][j] for s[i..j]), with centre expansion as the optimal
Complexity:O(N^3)→O(N^2)
Key Insight

Every palindrome has a centre: a letter for odd lengths, or the gap between two letters for even lengths. There are 2N − 1 centres. From each one, move L to the left and R to the right while the ends match. Each match is one palindrome. The first mismatch ends this centre, and no table is needed.

Core Validity Invariant

A range is a palindrome exactly when every pair from its centre outward matches, and the first mismatch ends the palindromes for that centre.

01

Problem

You are given a string s. Count its palindromic substrings. A substring is a run of letters that sit next to each other in s, and a palindrome reads the same forwards and backwards.

Each substring is counted by its position. If the letter a appears three times, that gives three single-letter palindromes, even though they are the same letter.

Input

A string s of lowercase letters.

Output

The number of substrings of s that are palindromes.

Example 1
Input: s = "abc"
Output: 3

The palindromes are a, b and c. No two letters side by side are equal, so nothing longer works.

Example 2
Input: s = "aaa"
Output: 6

They are a, a, a, aa, aa and aaa. The two aa substrings count separately because they start at different positions.

Constraints
  • • 1 ≤ s.length ≤ 1000
  • • s consists of lowercase English letters.
02

First, think yourself

Before opening any solution, try to answer these on paper. Use hints only when stuck — each hint is counted.

  1. 1.

    Is one letter by itself a palindrome? How many substrings does a string of length N have in total?

  2. 2.

    To check one substring, which pairs of letters must match? Where do the pairs start and end?

  3. 3.

    If s[l..r] is a palindrome, what must be true about the part inside it, s[l+1..r-1]?

  4. 4.

    Every palindrome has a middle. Can I list every possible middle, including the gaps between two letters?

  5. 5.

    If I count the palindromes around each middle by growing outward, can any palindrome be counted twice?

03

Brute force approach

04

Better approach

05

Optimized approach

06

Why does this algorithm work?

07

Dry run

08

All approaches compared

ApproachIdeaTimeSpace
Brute ForceTest each substring by comparing its ends, then the next pair insideO(N^3)O(1)
BetterisPal[i][j] says whether s[i..j] is a palindrome; each cell reads one cell inside itO(N^2)O(N^2)
OptimalFrom each of the 2N − 1 centres, grow outward while the ends matchO(N^2)O(1)
09

Why does this belong to this pattern?

Clues in the problem statement
  • “count the palindromic substrings”The answer is a count over every contiguous range, so there are about N² candidates to look at.
  • “substring (contiguous)”Each candidate is a range s[i..j]. Only its end pairs need to match.
  • “reads the same backward as forward”The rule is about mirrored pairs: s[i] = s[j], and then the inside must be a palindrome too.
Substring range s[i..j]+valid = ends match and the inside is valid+count every valid range→Palindrome DP (dp[i][j] for s[i..j]), with centre expansion as the optimal

This is the anchor of the palindrome sub-pattern. A range is a palindrome when its ends match and the range inside it is a palindrome. The table gives every range a cell for that rule, and centre expansion applies the same rule without storing anything. Longest Palindromic Substring uses the same rule but keeps one answer. This problem counts every palindrome, so every matching step adds one to the total instead of only the longest range being kept.

10

Don't make this mistake

Pattern confusion: only odd-length centres
✗ for (int i = 0; i < n; i++) { int l = i, r = i; while (l >= 0 && r < n && s[l] == s[r]) { count++; l--; r++; } }
✓ for (int centre = 0; centre < 2 * n - 1; centre++) { int l = centre / 2, r = l + centre % 2; while (l >= 0 && r < n && s[l] == s[r]) { count++; l--; r++; } }

Odd-length palindromes have a letter in the middle, but even-length ones have a gap in the middle, and only the right loop visits the gaps. On abba the wrong loop counts 4 (a, b, b, a) and misses bb and abba, so it gives 4 instead of 6. On the dry-run aabaa it gives 7 instead of 9.

Counting once per centre instead of once per match
✗ bool hit = false; while (l >= 0 && r < n && s[l] == s[r]) { hit = true; l--; r++; } if (hit) count++;
✓ while (l >= 0 && r < n && s[l] == s[r]) { count++; l--; r++; }

A centre with a long palindrome holds several palindromes, one for each step outward. On aaa the centre at the middle a gives a and aaa, which is 2, but the wrong version adds 1. The total becomes 5 instead of 6.

Table: forgetting that length 2 has an empty inside
✗ isPal[i][j] = s[i] == s[j] && isPal[i + 1][j - 1];
✓ isPal[i][j] = s[i] == s[j] && (len <= 2 || isPal[i + 1][j - 1]);

aa has nothing inside, so it is a palindrome. But isPal[i+1][i] is never filled, so it reads as false and aa is missed. On aaa the table gives 4 instead of 6, because both aa substrings are missed.

11

Want to implement this?

Write it yourself on LeetCode, then come back and compare your code with the solutions above.

Open coding workspace
How did it go?
Beyond The LeetCode Problem

How this concept connects to System Design & C++ Systems

Bridge DSA with production engineering

Top engineering interviews test whether you understand how algorithms like DP — Strings translate into production architectures and low-level memory mechanics.