{x}
blog image

Maximum Product of the Length of Two Palindromic Subsequences

Given a string s, find two disjoint palindromic subsequences of s such that the product of their lengths is maximized. The two subsequences are disjoint if they do not both pick a character at the same index.

Return the maximum possible product of the lengths of the two palindromic subsequences.

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. A string is palindromic if it reads the same forward and backward.

 

Example 1:

example-1
Input: s = "leetcodecom"
Output: 9
Explanation: An optimal solution is to choose "ete" for the 1st subsequence and "cdc" for the 2nd subsequence.
The product of their lengths is: 3 * 3 = 9.

Example 2:

Input: s = "bb"
Output: 1
Explanation: An optimal solution is to choose "b" (the first character) for the 1st subsequence and "b" (the second character) for the 2nd subsequence.
The product of their lengths is: 1 * 1 = 1.

Example 3:

Input: s = "accbcaxxcxx"
Output: 25
Explanation: An optimal solution is to choose "accca" for the 1st subsequence and "xxcxx" for the 2nd subsequence.
The product of their lengths is: 5 * 5 = 25.

 

Constraints:

  • 2 <= s.length <= 12
  • s consists of lowercase English letters only.

Solution Explanation: Maximum Product of the Length of Two Palindromic Subsequences

This problem asks us to find two disjoint palindromic subsequences within a given string s such that the product of their lengths is maximized. "Disjoint" means they share no characters at the same index.

The solution uses a clever approach combining bit manipulation and palindrome checking:

1. Bit Manipulation for Subsequences:

Each subsequence of s can be represented by a binary number where each bit corresponds to a character in s. A bit set to 1 indicates the character is included in the subsequence; 0 indicates exclusion. For example, if s = "abc", the binary number 101 represents the subsequence "ac". This allows us to efficiently iterate through all possible subsequences using bitwise operations.

2. Palindrome Check:

For each subsequence (represented by a binary number), we need to check if it's a palindrome. This is done efficiently by comparing characters from the beginning and end, moving inwards, only considering characters included in the subsequence (those with a 1 in the binary representation).

3. Finding Disjoint Palindromes:

The key is how we find disjoint palindromic subsequences. Let's say we have a palindromic subsequence i. We can use the bitwise XOR operation (^) to find its complement (mx = (1 << n) - 1 ^ i), where n is the length of s. This complement represents all the characters not in subsequence i. We then iterate through all subsequences within this complement to find another palindromic subsequence j that's disjoint from i.

4. Maximizing the Product:

We iterate through all possible subsequences i. If i is a palindrome, we find its complement and search for another palindromic subsequence j within the complement. We calculate the product of their lengths (number of 1s in their binary representations) and track the maximum product found.

Time Complexity Analysis:

  • Generating all subsequences takes O(2n) time, where n is the length of s.
  • Checking if a subsequence is a palindrome takes O(n) in the worst case.
  • Iterating through the complement to find a disjoint palindrome is also O(2n) in the worst case.

Therefore, the overall time complexity is dominated by the nested loops, resulting in approximately O(3n) in the worst case. The space complexity is O(2n) to store the palindrome flags for all subsequences.

Code Explanation (Python):

class Solution:
    def maxProduct(self, s: str) -> int:
        n = len(s)
        p = [True] * (1 << n)  # p[k] is True if subsequence k is a palindrome
 
        # Check for palindromes
        for k in range(1, 1 << n):
            i, j = 0, n - 1
            while i < j:
                while i < j and (k >> i & 1) == 0:  # Skip characters not in subsequence
                    i += 1
                while i < j and (k >> j & 1) == 0:
                    j -= 1
                if i < j and s[i] != s[j]:
                    p[k] = False
                    break
                i += 1
                j -= 1
 
        ans = 0
        for i in range(1, 1 << n):  # Iterate through all subsequences
            if p[i]:  # If i is a palindrome
                mx = ((1 << n) - 1) ^ i  # Complement of i
                j = mx
                a = i.bit_count()  # Length of subsequence i
                while j:  # Iterate through subsequences in the complement
                    if p[j]:  # If j is a palindrome
                        b = j.bit_count()  # Length of subsequence j
                        ans = max(ans, a * b)
                    j = (j - 1) & mx  # Efficiently iterate through the complement
        return ans

The Java, C++, and Go code follow a very similar logic and structure, adapting the bitwise operations and palindrome checks to their respective language features. The core algorithm remains the same.