A length-3 palindrome has the form aba where the first and third characters are the same. We can use recursion to generate all subsequences of length 3 and check which ones are palindromes. This explores all possible combinations by either including or excluding each character.
rec(i, cur) where i is the current index and cur is the current subsequence being built.cur has length 3:i reaches the end of the string, return.rec(0, "") and return the size of the set.Where is the length of the string and is the number of unique three length pallindromic subsequences (26 * 26 = 676).
Instead of generating all subsequences recursively, we can use three nested loops to pick positions for the three characters. For indices i < j < k, we check if s[i] == s[k] to form a palindrome. Using a set ensures we count each unique palindrome only once.
i from 0 to n-3:j from i+1 to n-2:k from j+1 to n-1:s[i] == s[k], add the string s[i] + s[j] + s[k] to the set.Where is the length of the string and is the number of unique three length pallindromic subsequences (26 * 26 = 676).
Since we only have 26 lowercase letters, there are at most 26 * 26 = 676 possible palindromes of length 3 (26 choices for the end characters, 26 for the middle). We can check each potential palindrome by scanning the string once to see if it exists as a subsequence.
res = 0.ends + mid + ends.3-character sequence in order.res.res.class Solution:
def countPalindromicSubsequence(self, s: str) -> int:
res = 0
for ends in range(ord('a'), ord('z') + 1):
for mid in range(ord('a'), ord('z') + 1):
seq = chr(ends) + chr(mid) + chr(ends)
idx, found = 0, 0
for c in s:
if seq[idx] == c:
idx += 1
if idx == 3:
found = 1
break
res += found
return resWhere is the length of the string and is the number of unique three length pallindromic subsequences (26 * 26 = 676).
Instead of checking all possible palindromes, we can iterate through the string and treat each position as a potential middle character. For each middle position, we need to know which characters appear both before and after it. A palindrome exists if the same character appears on both sides.
s[i] in the string:(s[i], letter) to the result set (representing the palindrome letter + s[i] + letter).s[i] to the left set.class Solution:
def countPalindromicSubsequence(self, s: str) -> int:
res = set()
left = set()
right = collections.Counter(s)
for i in range(len(s)):
right[s[i]] -= 1
if right[s[i]] == 0:
right.pop(s[i])
for j in range(26):
c = chr(ord('a') + j)
if c in left and c in right:
res.add((s[i], c))
left.add(s[i])
return len(res)Where is the length of the string and is the number of unique three length pallindromic subsequences (26 * 26 = 676).
We can precompute prefix counts for each character, allowing us to quickly determine how many of each character appear in any substring. For each possible end character, we find its first and last occurrence, then count distinct middle characters between them using prefix sums.
prefix[i][c] = count of character c in s[0..i-1].l = firstIndex and r = lastIndex.prefix[r][mid] - prefix[l+1][mid] > 0.class Solution:
def countPalindromicSubsequence(self, s: str) -> int:
n = len(s)
prefix = [[0] * 26 for _ in range(n + 1)]
firstIndex = [-1] * 26
lastIndex = [-1] * 26
for i in range(n):
j = ord(s[i]) - ord('a')
if firstIndex[j] == -1:
firstIndex[j] = i
lastIndex[j] = i
prefix[i + 1] = prefix[i][:]
prefix[i + 1][j] += 1
res = 0
for ends in range(26):
if firstIndex[ends] == -1 or firstIndex[ends] == lastIndex[ends]:
continue
l, r = firstIndex[ends], lastIndex[ends]
for mid in range(26):
if prefix[r][mid] - prefix[l + 1][mid] > 0:
res += 1
return resFor a length-3 palindrome with character c at both ends, we need at least two occurrences of c. The palindrome can use any character between the first and last occurrence of c as the middle. By finding the first and last index of each character, we can count the distinct characters in between.
c from 'a' to 'z':l and last index r of c in the string.c doesn't appear twice, skip it.l+1 and r-1 into a set.The previous approach uses a set to track distinct middle characters, which has some overhead. We can use a bitmask instead, where each bit represents whether a character has been seen. This provides O(1) operations for checking and adding characters.
l = firstIndex and r = lastIndex.mask = 0.l+1 to r-1:class Solution:
def countPalindromicSubsequence(self, s: str) -> int:
firstIndex = [-1] * 26
lastIndex = [-1] * 26
for i in range(len(s)):
j = ord(s[i]) - ord('a')
if firstIndex[j] == -1:
firstIndex[j] = i
lastIndex[j] = i
res = 0
for ends in range(26):
if firstIndex[ends] == -1 or firstIndex[ends] == lastIndex[ends]:
continue
l, r = firstIndex[ends], lastIndex[ends]
mask = 0
for i in range(l + 1, r):
c = ord(s[i]) - ord('a')
if mask & (1 << c):
continue
mask |= (1 << c)
res += 1
return resThe problem asks for the count of unique palindromic subsequences, not the total number of subsequences. A palindrome like "aba" should only be counted once even if it appears multiple times in the string at different positions. Failing to use a set or similar deduplication mechanism leads to overcounting.
A subsequence does not require consecutive characters. Some solutions incorrectly look for contiguous substrings of length 3 instead of subsequences where the three characters can have any number of characters between them. The characters just need to maintain their relative order.
For each pair of matching end characters, you need to count distinct middle characters between the first and last occurrence. Iterating through the entire substring for each end character pair works but can be slow. The optimal approach uses prefix sums or bitmasks to efficiently count distinct characters in any range.