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

<p>Return <em>the <strong>maximum</strong> possible <strong>product</strong> of the lengths of the two palindromic subsequences</em>.</p>

<p>A <strong>subsequence</strong> 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 <strong>palindromic</strong> if it reads the same forward and backward.</p>

<p>&nbsp;</p>
<p><strong class="example">Example 1:</strong></p>
<img alt="example-1" src="https://assets.leetcode.com/uploads/2021/08/24/two-palindromic-subsequences.png" style="width: 550px; height: 124px;" />
<pre>
<strong>Input:</strong> s = &quot;leetcodecom&quot;
<strong>Output:</strong> 9
<strong>Explanation</strong>: An optimal solution is to choose &quot;ete&quot; for the 1<sup>st</sup> subsequence and &quot;cdc&quot; for the 2<sup>nd</sup> subsequence.
The product of their lengths is: 3 * 3 = 9.
</pre>

<p><strong class="example">Example 2:</strong></p>

<pre>
<strong>Input:</strong> s = &quot;bb&quot;
<strong>Output:</strong> 1
<strong>Explanation</strong>: An optimal solution is to choose &quot;b&quot; (the first character) for the 1<sup>st</sup> subsequence and &quot;b&quot; (the second character) for the 2<sup>nd</sup> subsequence.
The product of their lengths is: 1 * 1 = 1.
</pre>

<p><strong class="example">Example 3:</strong></p>

<pre>
<strong>Input:</strong> s = &quot;accbcaxxcxx&quot;
<strong>Output:</strong> 25
<strong>Explanation</strong>: An optimal solution is to choose &quot;accca&quot; for the 1<sup>st</sup> subsequence and &quot;xxcxx&quot; for the 2<sup>nd</sup> subsequence.
The product of their lengths is: 5 * 5 = 25.
</pre>

<p>&nbsp;</p>
<p><strong>Constraints:</strong></p>

<ul>
	<li><code>2 &lt;= s.length &lt;= 12</code></li>
	<li><code>s</code> consists of lowercase English letters only.</li>
</ul>

from functools import cache
class Solution:
    def maxProduct(self, s: str) -> int:
        def set1Generator(idx, subseq):
            nonlocal n, set1
            if idx == n:
                set1.append(subseq[:])
                return
            set1Generator(idx+1, subseq) # not include current one
            subseq[idx] = True
            set1Generator(idx+1, subseq) # include current one
            # backtrack
            subseq[idx] = False

        def set2Generator(idx, subseq, s1):
            nonlocal n, set2
            if idx == n:
                set2.append(subseq[:])
                return
            set2Generator(idx+1, subseq, s1) # not include current one
            if s1[idx]==False:
                subseq[idx] = True
                set2Generator(idx+1, subseq, s1) # include current one
                # backtrack
                subseq[idx] = False

        @cache
        def getMaxLen(substr):
            # dp(i,j) = dp(i+1,j-1)+2 if i==j else dp[i+1,j], dp[i,j-1]
            sLen   = len(substr)
            if sLen <=1:
                return sLen
            dp     = [[0]*sLen for i in range(sLen)]
            maxSeq = 1
            for i in range(sLen-1):
                dp[i][i]=1
                if substr[i]==substr[i+1]:
                    dp[i][i+1]=2
                    maxSeq = 2
            dp[sLen-1][sLen-1]=1
            for i in range(sLen-2, -1, -1): # n-2 .. 0
                for j in range(i+2, sLen):
                    if substr[i]==substr[j]:
                        dp[i][j]=dp[i+1][j-1]+2
                    else:
                        dp[i][j]=max(dp[i+1][j], dp[i][j-1])
                    maxSeq = max(maxSeq, dp[i][j])
            #print(f"\t{substr}:{dp}")
            return maxSeq

        def getStr (subseq):
            return ''.join([s[i] for i in range(n) if subseq[i]])

        # product: similar length
        n    = len(s)
        set1 = []
        set1Generator(0, [False]*n)
        maxLen = 1
        for s1 in set1:
            str1 = getStr(s1)
            len1 = getMaxLen(str1) # can be cached
            if len1 == 0:
                continue
            #print(f"s1: {str1}-{s1}, {len1}")
            if len1 * (n-len1) <= maxLen:
                continue
            set2 = []
            set2Generator(0, [False]*n, s1)
            for s2 in set2:
                str2 = getStr(s2)
                len2 = getMaxLen(str2)
                if len2==0:
                    continue
                #print(f"s2: {str2}-{s2}, {len2}")
                maxLen = max(maxLen, len1*len2)
        return maxLen

        
4698