{x}
blog image

Longest Palindromic Subsequence II

Solution Explanation: Longest Palindromic Subsequence II

This problem asks for the length of the longest "good" palindromic subsequence in a given string. A "good" subsequence is defined as:

  1. A subsequence of the input string.
  2. A palindrome (reads the same forwards and backward).
  3. Of even length.
  4. No two consecutive characters are the same, except possibly the middle two (in the case of an even-length palindrome, there are no consecutive characters).

The most efficient approach is using dynamic programming with memoization (or top-down DP). We avoid redundant calculations by storing the results of subproblems.

Approach: Top-Down Dynamic Programming with Memoization

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:

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

  2. 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 and Space Complexity

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

Code Examples (Python, Java, C++, Go)

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.