2 minute read

Problem Statement

problem-647

Intuition

To efficiently count these substrings, I plan to iterate through each character in the string and expand around it to check for both odd and even length palindromes.

Approach

I’ll define a helper function is_palindromic that takes two pointers and checks for palindromic substrings by expanding outward. Then, I’ll iterate through each character in the string, considering it both as the center of an odd-length palindrome and as the left part of an even-length palindrome. The helper function will be called for each case, and the count of palindromic substrings will be updated accordingly.

Complexity

  • Time complexity: O(n^2) where n is the length of the input string. The nested expansion loop can take O(n) time for each character in the worst case.

  • Space complexity: O(1) as we are using constant space for variables.

Code

class Solution:
    def countSubstrings(self, s: str) -> int:
        def is_palindromic(l, r):
            ans = 0
            while l >= 0 and r < len(s):
                if s[l] == s[r]:
                    ans += 1
                else:
                    break
                l -= 1
                r += 1
            return ans

        res = 0
        for i, c in enumerate(s):
            odd = is_palindromic(i, i)
            even = is_palindromic(i, i + 1)
            res += odd + even
        return res

Editorial Solution

Approach 1: Check All Substrings

def is_palindrome(l, r, s):
    while l <= r:
        if s[l] != s[r]:
            return False
        l += 1
        r -= 1
    return True

def count_palindromic_substrings(s):
    count = 0
    for i, c in enumerate(s):
        for j in range(i + 1):
            count += is_palindrome(j, i, s)

    return count
  • Time complexity: O(n^3)
  • Space complexity: O(1)

Approach 2: Dynamic Programming

def count_palindromic_substrings(s):
    count = 0

    # Initialize a lookup table of dimensions len(s) * len(s)
    dp = [[False for i in range(len(s))] for i in range(len(s))]

    # Base case: A string with one letter is always a palindrome
    for i in range(len(s)):
        dp[i][i] = True
        count += 1

    # Base case: Substrings of two letters
    for i in range(len(s)-1):
        dp[i][i + 1] = (s[i] == s[i + 1])
        # A boolean value is added to the count where True means 1 and False means 0
        count += dp[i][i + 1]

    # Substrings of lengths greater than 2
    for length in range(3, len(s)+1):
        i = 0
        # Checking every possible substring of any specific length
        for j in range(length - 1, len(s)):
            dp[i][j] = dp[i + 1][j - 1] and (s[i] == s[j])
            count += dp[i][j]
            i += 1

    return count
  • Time complexity: O(n^2)
  • Space complexity: O(n^2)

Approach 3: Expand Around Possible Centers

class Solution {
 public:
    int countSubstrings(string s) {
        int ans = 0;

        for (int i = 0; i < s.size(); ++i) {
            // odd-length palindromes, single character center
            ans += countPalindromesAroundCenter(s, i, i);

            // even-length palindromes, consecutive characters center
            ans += countPalindromesAroundCenter(s, i, i + 1);
        }

        return ans;
    }

    int countPalindromesAroundCenter(const string& ss, int lo, int hi) {
        int ans = 0;

        while (lo >= 0 and hi < ss.size()) {
            if (ss[lo] != ss[hi])
                break;      // the first and last characters don't match!

            // expand around the center
            lo--;
            hi++;

            ans++;
        }

        return ans;
    }
};