Longest Palindromic Subsequence

LeetCode problem 516. Longest Palindromic Subsequence.

## Problem description

Given a string `s`, find *the longest palindromic **subsequence**'s length in* `s`.

A **subsequence** is a sequence that can be derived from another sequence by deleting some or no elements without changing the order of the remaining elements.

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.

## Problem analysis

We can use dynamic programming to solve the problem, by starting from looking at substrings of input with length 1, and then repeatedly consider longer substrings that contain shorter substrings, since a palindromic subsequence in a shorter substring will also exist in all longer substrings that contain this shorter substring. For example, if a shorter substring has a palindromic subsequence with length 5, then all longer substrings containing this shorter substring will have a palindromic subsequence with length of at least 5.

* Example: "abcdeba" contains a subsequence of length 5 ("abcba"). Then, since "eabcdebad" contains "abcdeba", the former string also has a palindromic subsequence with at least length 5.

Now we want to find out the transition relationships.

Let `len` be the length of input. We can create a table `dp` with `len` rows and `len` columns. Let `dp[left][right]` represent the length of longest palindromic subsequence in `input[left..right]`.

* Base case 1: For all substrings of length 1, the longest palindromic subsequence has length 1 (the substrings themselves).
* Base case 2: For all substrings of length 2:
	+ If `input[left] == input[right]` then `dp[left][right] = 2`.
	+ Otherwise `dp[left][right] = 1`.
* Transition equations for substrings of length 3 or longer:
	+ If `input[left] == input[right]`, then we look at `dp[left+1][right-1]`, i.e. the longest palindromic subsequence in the substring excluding `left` and `right`, and then add 2 to get `dp[left][right]`. So `dp[left][right] = dp[left+1][right-1] + 2`.
	+ Otherwise, putting in `input[left]` and `input[right]` will not create a longer palindromic subsequence than the longest palindromic subsequence in `input[left+1..right-1]`. But the substrings `input[left..right-1]` or `input[left+1..right]` may contain a longest palindromic subsequence longer than that in `input[left+1..right-1]`. So `dp[left][right] = max(dp[left][right-1], dp[left+1][right])`.

We can fill in the table starting from substrings of length 1, and proceed step by step until we reach the substring length of `len`. Finally the result will be `dp[0][len-1]`.

### Working example

* Input: "eabcdebad"

* Output: `dp[0][8] = 5` ("abcba")

### Complexity analysis

Let `len` be the length of input string.

Space complexity
Since we created the `dp` table with `len` rows and `len` columns, the space complexity is **O(`len`2)**.
Time complexity
Since the amount to fill in each cell in the `dp` table is constant, the total time complexity is also **O(`len`2)**.
## Solution

### C++ solution

```
class Solution {
public:
 int longestPalindromeSubseq(const std::string& s) {
 const int len = s.length();
 if (len <= 1) {
 return len;
 }

 // dp[left][right] means the length of longest palindromic substring
 // in s[left..right]
 int dp[len][len];
 memset(dp, 0, sizeof dp);
 for (int subLen = 1; subLen <= len; ++subLen) {
 for (int left = 0; left <= len - subLen; ++left) {
 int right = left + subLen - 1;

 if (subLen == 1) {
 dp[left][right] = 1;
 }
 else if (subLen == 2) {
 dp[left][right] = s[left] == s[right] ? 2 : 1;
 }
 else {
 // subLen >= 3
 if (s[left] == s[right]) {
 dp[left][right] = dp[left+1][right-1] + 2;
 }
 else {
 dp[left][right] = std::max(dp[left+1][right], dp[left][right-1]);
 }
 }
 }
 }

 return dp[0][len-1];
 }
};

```

### Java solution

```
class Solution {
 public int longestPalindromeSubseq(String s) {
 final int len = s.length();
 if (len <= 1) {
 return len;
 }

 // dp[left][right] means the length of longest palindromic substring
 // in s[left..right]
 int[][] dp = new int[len][len];
 for (int subLen = 1; subLen <= len; ++subLen) {
 for (int left = 0; left <= len - subLen; ++left) {
 int right = left + subLen - 1;

 if (subLen == 1) {
 dp[left][right] = 1;
 }
 else if (subLen == 2) {
 dp[left][right] = s.charAt(left) == s.charAt(right) ? 2 : 1;
 }
 else {
 // subLen >= 3
 if (s.charAt(left) == s.charAt(right)) {
 dp[left][right] = dp[left+1][right-1] + 2;
 }
 else {
 dp[left][right] = Integer.max(dp[left+1][right], dp[left][right-1]);
 }
 }
 }
 }

 return dp[0][len-1];
 }
}

```

## See also

* Longest Palindromic Subsequence II
* Longest Palindromic Substring
* Palindromic Substrings
* Minimum Insertion Steps to Make a String Palindrome