Given a string s
, return the number of unique palindromes of length three that are a subsequence of s
.
Note that even if there are multiple ways to obtain the same subsequence, it is still only counted once.
A palindrome is a string that reads the same forwards and backwards.
A subsequence of a string is a new string generated from the original string with some characters (can be none) deleted without changing the relative order of the remaining characters.
"ace"
is a subsequence of "abcde"
.
Example 1:
Input: s = "aabca" Output: 3 Explanation: The 3 palindromic subsequences of length 3 are: - "aba" (subsequence of "aabca") - "aaa" (subsequence of "aabca") - "aca" (subsequence of "aabca")
Example 2:
Input: s = "adc" Output: 0 Explanation: There are no palindromic subsequences of length 3 in "adc".
Example 3:
Input: s = "bbcbaba" Output: 4 Explanation: The 4 palindromic subsequences of length 3 are: - "bbb" (subsequence of "bbcbaba") - "bcb" (subsequence of "bbcbaba") - "bab" (subsequence of "bbcbaba") - "aba" (subsequence of "bbcbaba")
Constraints:
3 <= s.length <= 105
s
consists of only lowercase English letters.This problem asks to find the number of unique palindromic subsequences of length 3 within a given string s
. The solution leverages the observation that a length-3 palindrome is of the form XYX
, where X
and Y
are characters. We can optimize the search by focusing on the outer characters (X
).
Iterate Through Outer Characters: The solution iterates through each lowercase English letter ('a' to 'z'). Each letter is considered as a potential outer character (X
) of the palindrome.
Find First and Last Occurrences: For each outer character X
, we find its first (l
) and last (r
) occurrences in the string s
using s.find(c)
and s.rfind(c)
(or their equivalents in other languages).
Check for Valid Palindromes: If the last occurrence (r
) is more than one position after the first occurrence (l
), this implies there's space for at least one inner character (Y
). This is our condition for a possible length-3 palindrome. If r - l <= 1
, there's no space for an inner character, so we proceed to the next outer character.
Count Unique Inner Characters: If a valid range exists (r - l > 1
), the code then iterates through the characters between the first and last occurrence of X
(s[l + 1:r]
in Python). It uses a set
(or a bitmask) to efficiently count the number of unique inner characters (Y
) in this range. Each unique inner character forms a distinct palindrome (XYX
).
Accumulate Count: The count of unique inner characters for each outer character is added to the total count of unique length-3 palindromic subsequences.
Return Total Count: Finally, the function returns the accumulated count.
Time Complexity: The outer loop iterates 26 times (for each lowercase letter). The inner loop (finding unique characters) iterates at most n
times in the worst-case scenario (where n
is the length of the string s
). Thus, the overall time complexity is O(26n), which simplifies to O(n). The use of set
(or a bitmask) ensures that counting unique characters is done in near-constant time on average.
Space Complexity: The space used is dominated by the set
(or bitmask) to store unique characters, which takes O(26) space in the worst case. This is constant space, O(1), independent of the input string length.
The provided code examples in Python, Java, C++, Go, TypeScript, Javascript, and C# all implement this approach, using a set
or bit manipulation for efficient unique character counting.
Python (using set):
def countPalindromicSubsequence(s: str) -> int:
ans = 0 # Initialize the count of unique palindromic subsequences
for c in ascii_lowercase: # Iterate through lowercase letters ('a' to 'z')
l, r = s.find(c), s.rfind(c) # Find first and last occurrences of character c
if r - l > 1: # Check if there's space for an inner character
ans += len(set(s[l + 1:r])) # Count unique inner characters and add to ans
return ans
Java (using bitmask):
class Solution {
public int countPalindromicSubsequence(String s) {
int ans = 0;
for (char c = 'a'; c <= 'z'; ++c) {
int l = s.indexOf(c), r = s.lastIndexOf(c);
int mask = 0; // Bitmask to track unique inner characters
for (int i = l + 1; i < r; ++i) {
int j = s.charAt(i) - 'a';
if ((mask >> j & 1) == 0) { // Check if character is already seen
mask |= 1 << j; // Add character to the bitmask
++ans;
}
}
}
return ans;
}
}
The other language examples follow a similar structure and logic. The choice between set
and bitmask is mainly a matter of preference or potential minor performance differences depending on the language and context. Both achieve the goal of efficiently counting unique characters.