Count Palindromic Subsequences
HardPrompt
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 <= 104sconsists of digits.
Approaches
3 approaches with complexity analysis and trade-offs.
A palindromic subsequence of length 5 has the structure xyzyx. A straightforward approach is to iterate through all possible indices for the inner pair yy. Let these indices be j and l. For each such pair where s[j] == s[l], we then count how many ways we can choose the outer pair xx and the middle element z.
The number of choices for z is simply the number of characters between j and l. The number of ways to choose xx can be found by using precomputed counts of each digit in the prefixes and suffixes of the string.
Algorithm
- A palindromic subsequence of length 5 has the form
xyzyx. - This approach focuses on finding the inner pair
yyat indicesjandl. - First, precompute two arrays:
prefix_counts[i][d]: the number of occurrences of digitdin the prefixs[0...i-1].suffix_counts[i][d]: the number of occurrences of digitdin the suffixs[i+1...n-1].- Both can be computed in
O(N*D)time, whereNis the string length andDis the number of digits (10).
- Initialize
total_count = 0. - Iterate through all possible indices
jfor the firstyfrom1ton-2. - In a nested loop, iterate through all possible indices
lfor the secondyfromj+1ton-2. - If
s.charAt(j) == s.charAt(l):- The number of choices for the middle element
zis the number of characters between indicesjandl, which isl - j - 1. - The number of ways to form the outer pair
xxis calculated by summing over all digitsdfrom 0 to 9:prefix_counts[j][d] * suffix_counts[l][d]. - The contribution for this pair
(j, l)is(l - j - 1) * (sum of pairs for x). - Add this contribution to
total_count, taking modulo at each step.
- The number of choices for the middle element
Walkthrough
This approach iterates through all possible pairs of indices (j, l) to serve as the positions for the inner y characters of the palindrome xyzyx. For this to be valid, we must have j < l and s[j] == s[l].
For each valid (j, l) pair, we calculate the number of ways to complete the palindrome:
- Choosing
z: The middle elementzcan be any characters[k]wherej < k < l. The number of choices isl - j - 1. - Choosing
x: The outerxcharacters must be at indicesiandmsuch thati < jandm > l, withs[i] == s[m]. To count these, we can sum over all possible digitsd('0' through '9'). For eachd, the number of ways to choose the outer pair is(count of d before index j) * (count of d after index l).
To get these counts efficiently, we precompute prefix_counts[i][d] and suffix_counts[i][d]. The main part of the algorithm involves three nested loops: one for j, one for l, and one for the digit d to calculate the number of x pairs. The total count is the sum of contributions from all valid (j, l) pairs.
class Solution { public int countPalindromicSubsequences(String s) { int n = s.length(); int MOD = 1_000_000_007; if (n < 5) { return 0; } int[][] prefix_counts = new int[n][10]; int[][] suffix_counts = new int[n][10]; int[] s_digits = new int[n]; for(int i=0; i<n; i++) s_digits[i] = s.charAt(i) - '0'; // Precompute prefix counts prefix_counts[0][s_digits[0]] = 1; for (int i = 1; i < n; i++) { for (int d = 0; d < 10; d++) { prefix_counts[i][d] = prefix_counts[i - 1][d]; } prefix_counts[i][s_digits[i]]++; } // Precompute suffix counts suffix_counts[n - 1][s_digits[n - 1]] = 1; for (int i = n - 2; i >= 0; i--) { for (int d = 0; d < 10; d++) { suffix_counts[i][d] = suffix_counts[i + 1][d]; } suffix_counts[i][s_digits[i]]++; } long totalCount = 0; for (int j = 1; j < n - 1; j++) { for (int l = j + 1; l < n - 1; l++) { if (s_digits[j] == s_digits[l]) { long num_z = l - j - 1; if (num_z <= 0) continue; long num_x_pairs = 0; for (int d = 0; d < 10; d++) { long count_before_j = (j > 0) ? prefix_counts[j - 1][d] : 0; long count_after_l = (l < n - 1) ? suffix_counts[l + 1][d] : 0; num_x_pairs = (num_x_pairs + count_before_j * count_after_l) % MOD; } totalCount = (totalCount + num_z * num_x_pairs) % MOD; } } } return (int) totalCount; }}Complexity
Time
O(N^2 * D). The precomputation takes O(N * D). The main part consists of two nested loops for `j` and `l` (O(N^2)) and an inner loop for digits (O(D)). This is too slow for N = 10^4.
Space
O(N * D), where N is the length of the string and D is the number of possible digits (10). This is for storing the prefix and suffix count arrays.
Trade-offs
Pros
Conceptually simple and a direct translation of the counting problem.
Easier to implement than more optimized solutions.
Cons
The time complexity is too high for the given constraints, leading to a Time Limit Exceeded (TLE) error.
Solutions
Solution
class Solution {private static final int MOD = (int)1 e9 + 7;public int countPalindromes(String s) { int n = s.length(); int[][][] pre = new int[n + 2][10][10]; int[][][] suf = new int[n + 2][10][10]; int[] t = new int[n]; for (int i = 0; i < n; ++i) { t[i] = s.charAt(i) - '0'; } int[] c = new int[10]; for (int i = 1; i <= n; ++i) { int v = t[i - 1]; for (int j = 0; j < 10; ++j) { for (int k = 0; k < 10; ++k) { pre[i][j][k] = pre[i - 1][j][k]; } } for (int j = 0; j < 10; ++j) { pre[i][j][v] += c[j]; } c[v]++; } c = new int[10]; for (int i = n; i > 0; --i) { int v = t[i - 1]; for (int j = 0; j < 10; ++j) { for (int k = 0; k < 10; ++k) { suf[i][j][k] = suf[i + 1][j][k]; } } for (int j = 0; j < 10; ++j) { suf[i][j][v] += c[j]; } c[v]++; } long ans = 0; for (int i = 1; i <= n; ++i) { for (int j = 0; j < 10; ++j) { for (int k = 0; k < 10; ++k) { ans += (long)pre[i - 1][j][k] * suf[i + 1][j][k]; ans %= MOD; } } } return (int)ans; }}Video walkthrough
Newsletter
One sharp idea, every week
System design and interview prep — short enough to finish.
No spam. Unsubscribe anytime.
Practice
Same difficulty — related problems to reinforce the pattern.