Recommended resources
The freely available resources this lesson will direct you to.
Welcome back! In our last lesson, we mastered the technique of reconstructing the Longest Common Subsequence (LCS) by backtracking through a completed DP table. We saw that the table is not just for finding a length, but a roadmap of the optimal choices made.
Today, we'll apply this powerful LCS pattern to solve a new, though related, problem: finding the Longest Palindromic Subsequence (LPS). This lesson will show you a classic and elegant problem-solving technique in dynamic programming: reducing a new problem to one you already know how to solve. We will demonstrate that finding the LPS of a string is equivalent to finding the LCS between the string and its reverse.
1. Defining the Problem: Longest Palindromic Subsequence
First, let's be clear about what we're looking for. A subsequence is formed by deleting zero or more characters from a string while maintaining the relative order of the remaining characters. A palindrome is a sequence that reads the same forwards and backward.
Therefore, the Longest Palindromic Subsequence is the longest possible subsequence of a string that is also a palindrome.

At first glance, this seems like a new DP problem requiring its own recurrence relation. While that's one way to solve it, there's a much more direct and insightful approach that leverages our work from the previous lessons.
2. The Core Insight: Reducing LPS to LCS
The key insight is this:
The length of the longest palindromic subsequence of a string
sis identical to the length of the longest common subsequence betweensand its reverse,reverse(s).
This is a powerful reduction. It means we don't need to invent a new algorithm; we can simply reuse the LCS algorithm we've already mastered.
But why does this work? Let's build the intuition. A sequence p is a palindrome if p is equal to reverse(p).
Consider a string s and its reverse, s_rev. Any subsequence that is common to both s and s_rev must have a special property. The following video provides an excellent and intuitive explanation for why any common subsequence between a string and its reverse must be a palindrome.
Longest Palindromic Subsequence - Leetcode 516 - Python
This video from NeetCodeIO clearly explains the logic behind the LCS reduction.
Please watch the segment from the explanation of the clever approach. The presenter gives a clear, informal proof of why finding the LCS of a string and its reverse guarantees you find a palindrome. Pay close attention to the argument he makes around the aab...baa example.
To summarize the argument from the video:
- Let's say we have a string
s. We creates_rev = reverse(s). - Now, we find the LCS of
sands_rev. Let's call this LCS resultp. - By definition,
pis a subsequence ofs. - Also by definition,
pis a subsequence ofs_rev. - If
pis a subsequence ofs_rev, it means that the characters ofpappear insin reverse order. - For
pto be a subsequence of boths(forward order) ands_rev(reverse order),pmust be equal to its own reverse. - Therefore, any common subsequence
pmust be a palindrome. The Longest Common Subsequence algorithm will naturally find the Longest Palindromic Subsequence.
3. Implementation: A Simple Wrapper
Now that we understand the "why," the "how" becomes incredibly straightforward. We can implement the longestPalindromeSubseq function as a simple wrapper around our existing longestCommonSubsequence function.
This article provides several implementations, but the core idea is the same across all of them.
Longest Palindromic Subsequence | Leetcode 516 - Code and Debug
This article from Code and Debug gets straight to the point, showing how LPS reduces to LCS and then presenting different ways to code the LCS part.
First, read the initial problem statement and approach to solidify the core idea. Then, skip down to the "Tabulation" section and examine the Code Implementation. Notice how the longestPalindromeSubseq function is just a few lines: it creates the reversed string and calls the longestCommonSubsequence helper function. This is the pattern you'll use.
Let's write out the complete code. We'll include the tabulation-based LCS length function from our previous studies for context.
def longest_common_subsequence_length(text1: str, text2: str) -> int:
"""Calculates the length of the LCS of two strings using bottom-up DP."""
m, n = len(text1), len(text2)
# dp[i][j] will be the length of LCS of text1[0...i-1] and text2[0...j-1]
dp = [[0] * (n + 1) for _ in range(m + 1)]
for i in range(1, m + 1):
for j in range(1, n + 1):
if text1[i-1] == text2[j-1]:
dp[i][j] = 1 + dp[i-1][j-1]
else:
dp[i][j] = max(dp[i-1][j], dp[i][j-1])
return dp[m][n]
def longest_palindrome_subseq_length(s: str) -> int:
"""
Calculates the length of the LPS by reducing it to an LCS problem.
"""
s_rev = s[::-1] # Python's slice notation to reverse a string
return longest_common_subsequence_length(s, s_rev)
# Let's test with the example from the image
s1 = "BANANA"
print(f"The length of the LPS for '{s1}' is: {longest_palindrome_subseq_length(s1)}") # Expected: 5 ("ANANA")
s2 = "bbbab"
print(f"The length of the LPS for '{s2}' is: {longest_palindrome_subseq_length(s2)}") # Expected: 4 ("bbbb")
s3 = "cbbd"
print(f"The length of the LPS for '{s3}' is: {longest_palindrome_subseq_length(s3)}") # Expected: 2 ("bb")
The time complexity is determined entirely by the LCS algorithm, which is where is the length of the string s. The space complexity for this version is also due to the DP table.
4. Reconstructing the Palindrome and Space Optimization
Just like with the standard LCS problem, we can do more than just find the length.
Reconstructing the LPS String:
If you need to find the actual palindromic subsequence string, you can reuse the reconstruction logic from our previous lesson. You would simply call your get_lcs_string function with the original string and its reverse: get_lcs_string(s, s_rev). The full DP table is required for the backtracking path.
Space Optimization:
In an interview, if you are only asked for the length of the LPS, you can use the space-optimized version of the LCS algorithm. This reduces the space complexity from to by only keeping track of the previous and current rows of the DP table.
The Code and Debug article you read earlier has a section on this.
Longest Palindromic Subsequence | Leetcode 516 - Code and Debug
Revisiting the same article to see the optimized implementation.
Please read the section "Tabulation (Space Optimize)". This shows how to compute the LCS length using only two rows, which is a common and important optimization to know. Also, read the "Final takeaway", as it concisely summarizes the trade-offs between the different approaches.
This trade-off is crucial: the space optimization allows you to find the length efficiently but sacrifices the ability to reconstruct the subsequence string, as the full backtracking path information is lost.
Conclusion
In this lesson, we saw how a new problem, Longest Palindromic Subsequence, can be elegantly solved by transforming it into a familiar one, Longest Common Subsequence. This is a powerful problem-solving pattern that frequently appears in algorithm design.
Key Takeaways:
- LPS Definition: The Longest Palindromic Subsequence is the longest subsequence of a string that reads the same forwards and backward.
- The Core Reduction:
LPS(s)is equivalent toLCS(s, reverse(s)). Any common subsequence of a string and its reverse must be a palindrome. - Implementation: Solving LPS is as simple as creating a reversed copy of the input string and passing both strings to your existing LCS function.
- Complexity: The time complexity is . The space complexity is for the standard approach (which allows reconstruction) or for the space-optimized version (which only finds the length).
We are continuing to build a strong foundation in dynamic programming on sequences. In our next lesson, we will tackle another classic problem in this category: the Longest Increasing Subsequence, which will introduce yet another distinct DP pattern.