{x}
blog image

Unique Length-3 Palindromic Subsequences

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.

  • For example, "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.

Solution Explanation for 1930. Unique Length-3 Palindromic Subsequences

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).

Approach:

  1. 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.

  2. 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).

  3. 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.

  4. 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).

  5. Accumulate Count: The count of unique inner characters for each outer character is added to the total count of unique length-3 palindromic subsequences.

  6. Return Total Count: Finally, the function returns the accumulated count.

Time and Space Complexity Analysis:

  • 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.

Code Examples (with detailed comments):

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.