Teaching Kids Programming – Using Dynamic Programming to Count the Number of Palindrome Substrings


Teaching Kids Programming: Videos on Data Structures and Algorithms

Given a string, your task is to count how many palindromic substrings in this string. The substrings with different start indexes or end indexes are counted as different substrings even they consist of same characters.

Example 1:
Input: “abc”
Output: 3
Explanation: Three palindromic strings: “a”, “b”, “c”.

Example 2:
Input: “aaa”
Output: 6
Explanation: Six palindromic strings: “a”, “a”, “a”, “aa”, “aa”, “aaa”.

Note:
The input string length won’t exceed 1000.

Hints:
How can we reuse a previously computed palindrome to compute a larger palindrome?
If “aba” is a palindrome, is “xabax” and palindrome? Similarly is “xabay” a palindrome?

Complexity based hint:
If we use brute-force and check whether for every start and end position a substring is a palindrome we have O(n^2) start – end pairs and O(n) palindromic checks. Can we reduce the time for palindromic checks to O(1) by reusing some previous computation?

Bruteforce Algorithm to Count the Number of Palindrome Substrings

We may bruteforce all the pairs of substring indices (i, j) and check the substring s[i:j+1] to see if it is a palindrome – which can take O(N) time. The overall complexity is O(N^2*N) which is O(N^3).

1
2
3
4
5
6
7
8
9
10
class Solution:
    def countSubstrings(self, s: str) -> int:
        n = len(s)
        ans = 0
        for i in range(n):
            for j in range(i, n):
                a = s[i:j+1]
                if a == a[::-1]:
                    ans += 1
        return ans
class Solution:
    def countSubstrings(self, s: str) -> int:
        n = len(s)
        ans = 0
        for i in range(n):
            for j in range(i, n):
                a = s[i:j+1]
                if a == a[::-1]:
                    ans += 1
        return ans

In the above implementation, we reverse the substring and check for equality – we may also use the Two Pointer Algorithm to speed up the palindrome validation O(N/2).

Top Down Dynamic Programming ALgorithm to Count the Number of Palindrome Substrings

We can use Dynamic Programming Algorithm to cache the validation results for the palindrome substrings. This allows us to reduce the overall time complexity to O(N^2) and we need O(N^2) to compute all the substring palindrome validation, and O(1) to retrieve the result later.

If tex_f42760207bebbb3fbd0a4f9fbe28edf8 Teaching Kids Programming - Using Dynamic Programming to Count the Number of Palindrome Substrings algorithms dynamic programming python string teaching kids programming youtube video represents whether the S[i:j+1] is a palindrome, then we have the following Dynamic Programming Transistion Equation:

tex_ff2a47a15a4dfbd3c16ed229268ef892 Teaching Kids Programming - Using Dynamic Programming to Count the Number of Palindrome Substrings algorithms dynamic programming python string teaching kids programming youtube video
Initialize all F values to True.

We can implement the Top-Down Dynamic Programming via Recursion with Memoization. Remember to use @cache, i.e. @lru_cache(None) or @lru_cache(maxsize=None) to memorize the preivous values along the calculation.

We start the Dynamic Programming process top-down.

1
2
3
4
5
6
7
8
9
class Solution:
    def countSubstrings(self, s: str) -> int:
        @cache
        def f(i, j):
            if i > j:
                return True
            return s[i] == s[j] and f(i + 1, j - 1)
        n = len(s)
        return [f(i, j) for i in range(n) for j in range(i, n)].count(True)
class Solution:
    def countSubstrings(self, s: str) -> int:
        @cache
        def f(i, j):
            if i > j:
                return True
            return s[i] == s[j] and f(i + 1, j - 1)
        n = len(s)
        return [f(i, j) for i in range(n) for j in range(i, n)].count(True)

Bottom Up Dynamic Programming ALgorithm to Count the Number of Palindrome Substrings

We can store the F values in an array, and start computing the validation values bottom up.

1
2
3
4
5
6
7
8
class Solution:
    def countSubstrings(self, s: str) -> int:
        n = len(s)
        dp = [[True] * n for _ in range(n)]
        for i in range(n - 1, -1, -1):
            for j in range(i + 1, n):
                dp[i][j] = s[i] == s[j] and dp[i + 1][j - 1]
        return [dp[i][j] for i in range(n) for j in range(i, n)].count(True)
class Solution:
    def countSubstrings(self, s: str) -> int:
        n = len(s)
        dp = [[True] * n for _ in range(n)]
        for i in range(n - 1, -1, -1):
            for j in range(i + 1, n):
                dp[i][j] = s[i] == s[j] and dp[i + 1][j - 1]
        return [dp[i][j] for i in range(n) for j in range(i, n)].count(True)

The time complexity is O(N^2) and the space complexity is also O(N^2) for both Dynamic Programming Implementations to count the number of palindrome substrings in a given string.

See also: Algorithms to Count the Number of Palindromic Substrings

–EOF (The Ultimate Computing & Technology Blog) —

GD Star Rating
loading...
867 words
Last Post: Teaching Kids Programming - Using a Stack to Remove All Adjacent Duplicates In String
Next Post: Using Depth First Search Algorithm to Complete the Minimax Tree

The Permanent URL is: Teaching Kids Programming – Using Dynamic Programming to Count the Number of Palindrome Substrings

Leave a Reply