Longest Palindromic Subsequence
Solve this Problems, find the length of the longest subsequence of it that reads the same forwards and backwards — the characters don't need to be contiguous, just in the same relative order.
The two ends of the string decide everything at each step: if they're equal, both can anchor the palindrome and the problem shrinks to the interval strictly between them, plus 2; if they differ, at least one end has to be left out, so the best answer is whichever of "drop the left end" or "drop the right end" works out better. That's exactly the recurrence for the longest common subsequence between s and its own reverse — but expressing it directly as an interval DP over (i, j) pairs of the same string avoids building a second string entirely, and it's the shape that generalizes cleanly to interval-DP problems beyond palindromes.
Test Case 1:
Test Case 2:
Test Case 3:
Constraints
- ◆
0 ≤ s.length ≤ 12 - ◆
s consists of lowercase English letters only
Try the Dry Run
Don't just read the solution — watch it execute, one step at a time.
Approach & Solutions
Brute Force — Recursive Interval Shrinking
BruteLook at the string through its two end characters. If they match, they can both be part of the palindrome, and what's left to decide is the best palindromic subsequence strictly between them — so add 2 and recurse on the inner interval. If they don't match, at least one of them can't be part of the answer, so try dropping the left end and try dropping the right end, and keep whichever recursive result is longer. Repeating this choice at every interval explores every possible subsequence, but the same (i, j) intervals get revisited across many different call paths, since nothing is cached.
O(2ⁿ)O(n)1class Solution {
2 public int longestPalindromeSubseq(String s) {
3 return solve(s, 0, s.length() - 1);
4 }
5 private int solve(String s, int i, int j) {
6 if (i > j) return 0;
7 if (i == j) return 1;
8 if (s.charAt(i) == s.charAt(j)) {
9 return 2 + solve(s, i + 1, j - 1);
10 }
11 return Math.max(solve(s, i + 1, j), solve(s, i, j - 1));
12 }
13}Optimal — Bottom-Up Interval DP
OptimalCache the same recursion in a table instead of recomputing it: dp[i][j] holds the longest palindromic subsequence length within s[i..j]. Every single character is trivially a palindrome of length 1, which fills the diagonal. Then build up by increasing interval length — for each (i, j), if the end characters match, dp[i][j] = dp[i+1][j-1] + 2 (or just 1 when i+1 > j-1, i.e. the interval has length 2); otherwise dp[i][j] = max(dp[i+1][j], dp[i][j-1]). Because every wider interval only depends on strictly shorter ones already computed, filling by increasing length guarantees everything needed is ready. The final answer sits at dp[0][n-1].
O(n²)O(n²)1class Solution {
2 public int longestPalindromeSubseq(String s) {
3 int n = s.length();
4 int[][] dp = new int[n][n];
5 for (int i = 0; i < n; i++) dp[i][i] = 1;
6 for (int len = 2; len <= n; len++) {
7 for (int i = 0; i + len - 1 < n; i++) {
8 int j = i + len - 1;
9 if (s.charAt(i) == s.charAt(j)) {
10 dp[i][j] = (len == 2 ? 0 : dp[i + 1][j - 1]) + 2;
11 } else {
12 dp[i][j] = Math.max(dp[i + 1][j], dp[i][j - 1]);
13 }
14 }
15 }
16 return n == 0 ? 0 : dp[0][n - 1];
17 }
18}