Taro Logo

Count Palindromic Subsequences

Hard
Google logo
Google
1 view
Topics:
StringsDynamic Programming

Given a string of digits s, return the number of palindromic subsequences of s having length 5. Since the answer may be very large, return it modulo 109 + 7.

Note:

  • A string is palindromic if it reads the same forward and backward.
  • A subsequence is a string that can be derived from another string by deleting some or no characters without changing the order of the remaining characters.

Example 1:

Input: s = "103301"
Output: 2
Explanation:
There are 6 possible subsequences of length 5: "10330","10331","10301","10301","13301","03301".
Two of them (both equal to "10301") are palindromic.

Example 2:

Input: s = "0000000"
Output: 21
Explanation: All 21 subsequences are "00000", which is palindromic.

Example 3:

Input: s = "9999900000"
Output: 2
Explanation: The only two palindromic subsequences are "99999" and "00000".

Constraints:

  • 1 <= s.length <= 10^4
  • s consists of digits.

Solution


Brute Force Approach

A naive approach would involve generating all possible subsequences of length 5 from the given string s and then checking if each subsequence is a palindrome. If it is, we increment our count. The final count, modulo 109 + 7, would be the answer.

Code (Python)

def is_palindrome(s):
    return s == s[::-1]

def count_palindromic_subsequences_brute_force(s):
    n = len(s)
    count = 0
    mod = 10**9 + 7
    for i in range(n):
        for j in range(i + 1, n):
            for k in range(j + 1, n):
                for l in range(k + 1, n):
                    for m in range(l + 1, n):
                        subsequence = s[i] + s[j] + s[k] + s[l] + s[m]
                        if is_palindrome(subsequence):
                            count = (count + 1) % mod
    return count

# Example usage:
s = "103301"
print(count_palindromic_subsequences_brute_force(s))

Time Complexity

The brute-force approach iterates through all possible combinations of 5 indices in the string. Therefore, the time complexity is O(n5), where n is the length of the string.

Space Complexity

The space complexity is O(1), as we're only using a constant amount of extra space.

Optimal Solution: Dynamic Programming

To improve the time complexity, we can use dynamic programming. The key idea is to count the palindromic subsequences of length 5 by considering the first and last characters. We can build the solution iteratively. Let dp[i][j] be the number of palindromic subsequences of length j that start and end with digit i (0-9). We'll compute palindromes of length 1, 3, and then 5.

Algorithm

  1. Count occurrences: First, count the occurrences of each digit in the string.
  2. Length 1 palindromes: The number of palindromic subsequences of length 1 are the counts of each digit.
  3. Length 3 palindromes: Iterate through the string. For each digit at index i, iterate through all digits d from 0-9. If the digit d occurs before index i and after index i, then we can form a palindrome of length 3 with d as the first and last digits and the digit at index i as the middle digit.
  4. Length 5 palindromes: Similar to step 3, iterate through the string. For each digit at index i, iterate through all digits d from 0-9. If the number of palindromic subsequences of length 3 that start and end with digit d occurs before and after index i, then we can form a palindrome of length 5.

Code (Python)

def count_palindromic_subsequences(s):
    n = len(s)
    mod = 10**9 + 7
    
    ans = 0
    for digit in range(10):
        digit_str = str(digit)
        
        # Find indices of the digit
        indices = [i for i, c in enumerate(s) if c == digit_str]
        
        for i in range(len(indices)):
            for j in range(i + 1, len(indices)):
                l, r = indices[i], indices[j]
                
                # Count palindromes of the form digit + x + y + x + digit
                for mid_digit in range(10):
                    mid_digit_str = str(mid_digit)
                    mid_indices = [k for k in range(l + 1, r) if s[k] == mid_digit_str]
                    
                    for m1 in range(len(mid_indices)):
                        for m2 in range(m1+1, len(mid_indices)):
                            ans = (ans + 1) % mod
    return ans

# Example usage:
s = "103301"
print(count_palindromic_subsequences(s))

Time Complexity

The time complexity is O(n * 100), which simplifies to O(n) because the nested loops go from 0-9. This is a significant improvement over the brute-force approach.

Space Complexity

The space complexity is O(1), since we are using a fixed amount of extra space to store counts and indices.

Edge Cases

  • Empty String: If the input string is empty or has a length less than 5, the number of palindromic subsequences of length 5 is 0.
  • String with length 5: If the string length is exactly 5, check if the string is a palindrome and return 1 if it is, otherwise return 0.
  • String with repeating characters: The algorithm should correctly handle strings with repeating characters (e.g., "0000000").