We can check if a string is a palindrome in two ways:
- Inwards from the two ends
- Outwards from center

Both ways take time. However, there is a difference: the count of possible pairs of end points is
whereas the count of possible centers is
.
Process inwards from two ends
Brute force
Try all possible substrings.
Time: , space:
.
class Solution:
def countSubstrings(self, s: str) -> int:
def is_palindrome(s: str, l: int, r: int) -> bool:
while l < r:
if s[l] != s[r]:
return False
l, r = l+1, r-1
return True
n = len(s)
count = 0
for start in range(n):
for end in range(start, n):
count += (1 if is_palindrome(s, start, end) else 0)
return count
Dynamic programming
Using DP, for all possible substrings, we can compute if it is a palindrome in total time. Counting can piggyback on it.
Subproblem
Let denote if the substring
s[i:j+1] is a palindrome. Then:
Order
Size of a subproblem is , processing them in the order of increasing size gives a topological order.
Time: , space:
.
class Solution:
def countSubstrings(self, s: str) -> int:
n = len(s)
dp = [[False] * n for _ in range(n)]
for i in range(n):
dp[i][i] = True
count = n
for sz in range(2, n + 1):
for i in range(0, n - sz + 1):
j = i + sz - 1
dp[i][j] = (s[i] == s[j]) and (dp[i + 1][j - 1] if i + 2 <= j else True)
count += (1 if dp[i][j] else 0)
return count
Process outwards from center
Brute force
Time: , space:
.
class Solution:
def countSubstrings(self, s: str) -> int:
def count_palindromes(s: str, left: int, right: int):
n = len(s)
count = 0
while left >= 0 and right < n:
if s[left] != s[right]:
break
count, left, right = count+1, left-1, right+1
return count
n = len(s)
count = n
for c in range(n):
count += count_palindromes(s, c-1, c+1)
count += count_palindromes(s, c, c+1)
return count
Leave a comment