Longest Palindromic Subsequence

Updated on 05 June, 2025
Longest Palindromic Subsequence header image

Problem Statement

The objective is to determine the length of the longest palindromic subsequence within a given string s. A subsequence is defined as a sequence that can be derived from the string by removing some characters without rearranging the remaining characters. This problem emphasizes on identifying the longest sequence that reads the same forward and backward, irrespective of non-adjacent characters. Utilizing an understanding of palindromes in conjunction with dynamic programming or recursive solutions, the task involves optimizing search techniques to handle strings that can be as long as 1000 characters.

Examples

Example 1

Input:

s = "bbbab"

Output:

4

Explanation:

One possible longest palindromic subsequence is "bbbb".

Example 2

Input:

s = "cbbd"

Output:

2

Explanation:

One possible longest palindromic subsequence is "bb".

Constraints

  • 1 <= s.length <= 1000
  • s consists only of lowercase English letters.

Approach and Intuition

  1. Understanding Palindromes and Subsequences: A palindrome reads the same forwards and backward, such as "radar" or "level". A subsequence maintains the order of characters but not necessarily their adjacency. For example, the subsequence "abc" can be derived from "aibc" by dropping the 'i'.

  2. Initial Observations from Examples:

    • In Example 1 with s = "bbbab", the longest palindromic subsequence is "bbbb", which has a length of 4.
    • In Example 2 with s = "cbbd", the longest palindromic subsequence is "bb", which has a length of 2.

    These observations indicate that two consecutive identical characters ("bb") always provide a simple palindromic subsequence.

  3. Conceptual Approach:

    • Recursive Definition: Consider dp[i][j] as the length of the longest palindromic subsequence within the substring s[i:j+1]. If s[i] == s[j], then s[i] and s[j] contribute to the palindromic subsequence and can extend any palindrome from s[i+1] to s[j-1] by 2.
    • Base Cases:
      • If i > j, then dp[i][j] = 0 as an invalid substring.
      • If i == j, then dp[i][j] = 1 as a single character is trivially a palindrome.
    • Recurrence Relation:
      • If s[i] == s[j], then dp[i][j] = 2 + dp[i+1][j-1].
      • Otherwise, the palindrome could either be ending at j-1 or start after i, so dp[i][j] = max(dp[i+1][j], dp[i][j-1]).

    This recursive approach explores all sub-problems and builds the solution up to dp[0][n-1], where n is the length of the string.

  4. Optimization Using Dynamic Programming: Given the constraints, implementing this solution using dynamic programming helps to avoid redundant calculations and reduce the complexity from exponential to polynomial time, specifically O(n^2), where n is the length of string s.

The constraints ensure that every input string s is non-empty, with a length not exceeding 1000 and consisting only of lowercase English letters. This understanding supports handling the characterization of potential input values and corner cases, like minimal string lengths or strings with repetitive characters.

Solutions

  • C++
  • Java
  • Python
cpp
class Solution {
public:
    int longestPalindromeSubsequence(string str) {
        int length = str.length();
        vector<int> current(length), previous(length);

        for (int i = length - 1; i >= 0; --i) {
            current[i] = 1;
            for (int j = i + 1; j < length; ++j) {
                if (str[i] == str[j]) {
                    current[j] = previous[j - 1] + 2;
                } else {
                    current[j] = max(previous[j], current[j - 1]);
                }
            }
            previous = current;
        }

        return current[length - 1];
    }
};

The presented solution implements a function to determine the longest palindromic subsequence within a given string using dynamic programming in C++. This is managed through a bottom-up approach where each character is compared to the others to calculate the length of the longest subsequence that forms a palindrome.

  • Initialize two vectors current and previous, of sizes equal to the string length, to store intermediate results for dynamic programming.

  • Iterate through each character in the string in reverse order starting from the last character. During this, set current[i] to 1, recognizing that the smallest palindrome subsequence of a single character is one.

  • For each character, move forward through the string comparing to subsequent characters. When characters at position i and j match, set current[j] to previous[j-1] + 2. This action suggests that the inclusion of two matching characters increases the subsequence length by 2.

  • If characters do not match, update current[j] with the maximum value between previous[j] and current[j-1]. This decision ensures keeping the longest subsequence found so far, either by including or excluding the current character.

  • After iterating over the characters for specific i, update previous with the values from current to be used in the next iteration.

  • Finally, return current[length-1] which holds the length of the longest palindromic subsequence found in the entire string. This dynamic programming approach has an O(n^2) time complexity due to nested loops over the string length, where n is the length of the string.

This approach efficiently calculates the longest palindrome subsequence by updating and comparing subsequences iteratively, leveraging dynamic programming principles to store and reuse intermediate results.

java
class Solution {
    public int maxPalindromeSubseq(String str) {
        int len = str.length();
        int[] current = new int[len];
        int[] previous = new int[len];

        for (int i = len - 1; i >= 0; --i) {
            current[i] = 1;
            for (int j = i + 1; j < len; ++j) {
                if (str.charAt(i) == str.charAt(j)) {
                    current[j] = previous[j - 1] + 2;
                } else {
                    current[j] = Math.max(previous[j], current[j - 1]);
                }
            }
            previous = current.clone();
        }

        return current[len - 1];
    }
}

The solution is aimed to determine the length of the longest palindromic subsequence within a given string using a dynamic programming approach in Java. The process involves maintaining current and previous arrays to store interim results, optimizing the calculation by avoiding the recomputation typically associated with more naive methods.

  • Initialize two integer arrays current and previous to store the dynamic programming states for the current and previous iterations, respectively.
  • Traverse the input string in reverse to account for each character pairing without repetitions.
  • Use nested loops where the outer loop goes backward from the last character, and the inner loop goes forward from the current character of the outer loop.
    • If characters from the outer and inner loops match (str.charAt(i) == str.charAt(j)), set current[j] to the value of previous[j - 1] + 2.
    • If they do not match, set current[j] to the maximum value between previous[j] and current[j - 1] to include the best result observed so far.
  • After processing each pair, clone the current array into previous to prepare for the next iteration.
  • The last value of the current array after processing all characters (current[len - 1]) gives the length of the longest palindromic subsequence.

Utilize this efficient approach to solve for the longest palindromic subsequence by focusing on dynamic programming to manage past calculations effectively, ensuring that each step builds upon previously computed values.

python
class Solution:
    def longestPalindromicSubsequence(self, sequence: str) -> int:
        length = len(sequence)
        current_dp, previous_dp = [0] * length, [0] * length

        for i in range(length - 1, -1, -1):
            current_dp[i] = 1
            for j in range(i + 1, length):
                if sequence[i] == sequence[j]:
                    current_dp[j] = previous_dp[j - 1] + 2
                else:
                    current_dp[j] = max(previous_dp[j], current_dp[j - 1])
            previous_dp = current_dp[:]

        return current_dp[length - 1]

The solution provided addresses the problem of finding the length of the longest palindromic subsequence in a given string using a dynamic programming approach in Python. Here's a concise explanation of how the code works:

  • Initialize the length of the input sequence.
  • Use two lists, current_dp and previous_dp, to store intermediate results of dynamic programming. These lists help in avoiding the recomputation of states, which is essential for optimizing the solution.
  • Iterate through the sequence from the end to the beginning. For each character in the sequence, set the diagonal entry (indicating a subsequence of one character) to 1.
  • For each character comparison, check:
    • If the characters match, update the current_dp[j] with the value of previous_dp[j - 1] + 2.
    • If they don't match, take the maximum value between previous_dp[j] (indicating excluding the current character) and current_dp[j - 1] (indicating excluding the matched character).
  • Before moving to the next base character, copy the current_dp array to previous_dp to use it for the next iteration.
  • The last element of the current_dp array gives the length of the longest palindromic subsequence.

This method ensures an efficient computation with a time complexity of O(n^2) and a space complexity of O(n), where n is the length of the string. This approach is particularly effective for longer strings where a naive recursive approach would be too slow.

Comments

No comments yet.