This problem asks for the length of the longest "good" palindromic subsequence in a given string. A "good" subsequence is defined as:
The most efficient approach is using dynamic programming with memoization (or top-down DP). We avoid redundant calculations by storing the results of subproblems.
We define a recursive function dfs(i, j, x)
:
i
, j
: Indices defining the substring s[i...j]
of the input string s
we are currently considering.x
: The last character used in the current subsequence. This helps prevent consecutive identical characters.The function returns the length of the longest "good" palindromic subsequence within s[i...j]
ending with character x
.
The base case is when i >= j
(empty substring), returning 0.
The recursive steps are:
Matching Characters: If s[i] == s[j]
and s[i] != x
(meaning we don't have consecutive identical characters), we recursively call dfs(i + 1, j - 1, s[i])
to find the longest "good" subsequence within the inner substring s[i+1...j-1]
. We add 2 to this result because we're adding s[i]
and s[j]
to the subsequence.
Non-matching Characters: If s[i] != s[j]
, we recursively explore two possibilities:
dfs(i + 1, j, x)
: Exclude s[i]
dfs(i, j - 1, x)
: Exclude s[j]
We choose the maximum of these two results.To prevent redundant calculations, we use memoization (caching). A 3D array f
stores the results of dfs(i,j,x)
. If f[i][j][x]
is already computed, we return the cached value directly.
Time Complexity: O(n^2 * C), where n is the length of the string and C is the number of characters in the alphabet (26 for lowercase English letters). This is because the dfs
function can be called for all possible sub-strings (O(n^2)) and for each substring, there are C choices for the last character. Memoization significantly improves performance by reducing redundant calculations.
Space Complexity: O(n^2 * C) to store the memoization table f
.
The code examples in the original response provide well-commented implementations of the solution in Python, Java, C++, and Go, demonstrating the memoization approach efficiently. They follow the algorithm described above. The memoization significantly improves the runtime over a purely recursive approach.