Longest Palindromic Subsequence (LPS)
The Longest Palindromic Subsequence (LPS) problem is about finding the longest part of a string that reads the same way backward and forward. We can solve this problem using dynamic programming. We will use a 2D table to keep track of the results from smaller problems. This will help us find the length of the longest palindromic subsequence.
The main idea of LPS is to find characters that make a palindrome. We will skip characters that do not match. This method helps us reduce the search area.
In this article, we will talk about the Longest Palindromic Subsequence problem. We will explain what it is and why it is important in dynamic programming. We will go over different ways to solve this problem. This includes using dynamic programming in Java, Python, and C++. We will also discuss how to save space, use recursion with memoization, and use iterative methods. Finally, we will compare these different ways to solve the problem. Here are the topics we will cover:
- Dynamic Programming Longest Palindromic Subsequence Solution
- Understanding the Longest Palindromic Subsequence Problem
- Dynamic Programming Approach for Longest Palindromic Subsequence in Java
- Dynamic Programming Approach for Longest Palindromic Subsequence in Python
- Dynamic Programming Approach for Longest Palindromic Subsequence in C++
- Optimizing Space Complexity for Longest Palindromic Subsequence
- Recursive Approach with Memoization for Longest Palindromic Subsequence
- Iterative Approach for Longest Palindromic Subsequence
- Comparing Different Approaches for Longest Palindromic Subsequence
- Frequently Asked Questions
If you want to learn more about dynamic programming, you can check out these articles. They are about Dynamic Programming: Fibonacci Number and Dynamic Programming: Longest Common Subsequence.
Understanding the Longest Palindromic Subsequence Problem
The Longest Palindromic Subsequence (LPS) problem is about finding the longest subsequence in a string that is a palindrome. A palindrome is a word that reads the same from both sides. Examples are “racecar” and “level”. Our task is to find this subsequence in a quick way, especially when the string is long.
Problem Definition
We have a string s. Our job is to find out how long the
longest subsequence is that can be a palindrome. The subsequence does
not need to have characters next to each other. But it must keep the
original order.
Example
- Input:
s = "bbabcbcab" - Output:
7 - Explanation: The longest palindromic subsequence is “babcbab”.
Approach
- Dynamic Programming Table: We will use a 2D DP
table. Here,
dp[i][j]shows the length of the longest palindromic subsequence in the substrings[i...j]. - Base Case: If
i == j, thendp[i][j] = 1. A single character is always a palindrome. - Recurrence Relation:
- If characters match: If
s[i] == s[j], then we can saydp[i][j] = dp[i + 1][j - 1] + 2. - If they do not match: We take the bigger value, so
dp[i][j] = max(dp[i + 1][j], dp[i][j - 1]).
- If characters match: If
Complexity
- Time Complexity: O(n^2). Here n is the length of the string.
- Space Complexity: O(n^2) for the DP table.
This basic understanding of the Longest Palindromic Subsequence problem helps us to create solutions in different programming languages using dynamic programming methods.
Dynamic Programming Approach for Longest Palindromic Subsequence in Java
We can solve the Longest Palindromic Subsequence problem using dynamic programming in Java. We use a 2D array to save the lengths of palindromic subsequences. The idea is to create a solution by using values we already calculated.
Step-by-Step Approach:
Initialization: We make a 2D array
dpof sizen x n. Here,nis the length of the input string. We set alldp[i][i] = 1because a single character is a palindrome of length 1.Filling the DP Table:
We go through the length of subsequences from 2 to
n.For each length, we check the starting index
iand find the ending indexj = i + length - 1.If the characters at
iandjare the same, we do:dp[i][j] = dp[i + 1][j - 1] + 2If they are different, we take the bigger of the two options:
dp[i][j] = Math.max(dp[i + 1][j], dp[i][j - 1])
Result: The length of the longest palindromic subsequence is in
dp[0][n - 1].
Java Code Implementation:
public class LongestPalindromicSubsequence {
public static int longestPalindromicSubseq(String s) {
int n = s.length();
int[][] dp = new int[n][n];
// Every single character is a palindrome
for (int i = 0; i < n; i++) {
dp[i][i] = 1;
}
// Build the table
for (int length = 2; length <= n; length++) {
for (int i = 0; i < n - length + 1; i++) {
int j = i + length - 1;
if (s.charAt(i) == s.charAt(j)) {
dp[i][j] = dp[i + 1][j - 1] + 2;
} else {
dp[i][j] = Math.max(dp[i + 1][j], dp[i][j - 1]);
}
}
}
return dp[0][n - 1];
}
public static void main(String[] args) {
String s = "bbbab";
System.out.println("Length of Longest Palindromic Subsequence: " + longestPalindromicSubseq(s));
}
}Explanation of the Code:
- The method
longestPalindromicSubseqfinds the length of the longest palindromic subsequence in the strings. - The dynamic programming table
dpkeeps the lengths of palindromic subsequences for different parts of the string. - We print the result in the
mainmethod, showing how it works with an example input.
This approach has time complexity of O(n^2) and space complexity of O(n^2). If you want to read more about dynamic programming, you can check articles like Dynamic Programming - Coin Change.
Dynamic Programming Approach for Longest Palindromic Subsequence in Python
We can solve the Longest Palindromic Subsequence (LPS) problem using dynamic programming in Python. We will use a 2D array to keep track of the lengths of palindromic subsequences.
Algorithm Steps:
- Initialize a 2D array: We create a 2D array called
dpwith sizen x n. Here,nis the length of the input string. Eachdp[i][j]will show the length of the longest palindromic subsequence from indexitoj. - Base case: Every single character is a palindrome
with length 1. So, we set
dp[i][i] = 1for alli. - Build the table: We go through the substring
lengths and fill the
dptable:- If the characters at both ends of the substring are the same, we
expand the palindrome:
dp[i][j] = dp[i + 1][j - 1] + 2. - If they are different, we take the maximum by excluding either
character:
dp[i][j] = max(dp[i + 1][j], dp[i][j - 1]).
- If the characters at both ends of the substring are the same, we
expand the palindrome:
- Result: The length of the longest palindromic
subsequence for the whole string will be at
dp[0][n - 1].
Python Code:
def longest_palindromic_subsequence(s: str) -> int:
n = len(s)
dp = [[0] * n for _ in range(n)]
# Base case: single character palindromes
for i in range(n):
dp[i][i] = 1
# Fill the dp table
for length in range(2, n + 1): # Length of the substring
for i in range(n - length + 1):
j = i + length - 1
if s[i] == s[j]:
dp[i][j] = dp[i + 1][j - 1] + 2
else:
dp[i][j] = max(dp[i + 1][j], dp[i][j - 1])
return dp[0][n - 1]
# Example usage
s = "bbabcbcab"
print("Length of Longest Palindromic Subsequence is:", longest_palindromic_subsequence(s)) # Output: 7Complexity:
- Time Complexity: O(n^2). Here,
nis the length of the string. - Space Complexity: O(n^2) because of the 2D
dparray.
We use this dynamic programming method to find the longest palindromic subsequence length in Python. This makes it good for medium-level problems in competitive programming and interviews. For more information about dynamic programming, you can check articles like Dynamic Programming - Longest Common Subsequence.
Dynamic Programming Approach for Longest Palindromic Subsequence in C++
We can solve the Longest Palindromic Subsequence (LPS) problem using dynamic programming. This problem is to find the longest subsequence in a string that reads the same from start to end and end to start.
C++ Implementation
Here is a simple C++ code to solve the LPS problem using dynamic programming:
#include <iostream>
#include <vector>
#include <string>
using namespace std;
int longestPalindromicSubsequence(const string &s) {
int n = s.length();
vector<vector<int>> dp(n, vector<int>(n, 0));
// Every single character is a palindrome of length 1
for (int i = 0; i < n; i++) {
dp[i][i] = 1;
}
// Build the DP table
for (int length = 2; length <= n; length++) {
for (int i = 0; i < n - length + 1; i++) {
int j = i + length - 1;
if (s[i] == s[j]) {
dp[i][j] = 2 + dp[i + 1][j - 1];
} else {
dp[i][j] = max(dp[i + 1][j], dp[i][j - 1]);
}
}
}
return dp[0][n - 1]; // The length of the longest palindromic subsequence
}
int main() {
string s = "bbabcbcab";
cout << "Length of Longest Palindromic Subsequence: "
<< longestPalindromicSubsequence(s) << endl;
return 0;
}Explanation of the Code
- Initialization: We create a 2D vector
dpto store lengths of palindromic subsequences. Each single character is set to 1 because it is a palindrome by itself. - Filling the DP Table: We go through all possible
lengths of substrings. If the characters at both ends of the substring
are the same, we update
dp[i][j]to2 + dp[i + 1][j - 1]. If they are not the same, we take the bigger value from the two possible substrings by removing either the first or the last character. - Result: The answer for the length of the longest
palindromic subsequence is at
dp[0][n-1], which covers the whole string.
This dynamic programming method runs in O(n^2) time and uses O(n^2) space. It is good for strings that are not too long. For better performance, we can look into ways to reduce space usage.
For more learning about dynamic programming methods, you can see the Dynamic Programming - Longest Common Subsequence article.
Optimizing Space Complexity for Longest Palindromic Subsequence
When we solve the Longest Palindromic Subsequence (LPS) problem using dynamic programming, we often make a 2D array. This array helps us store the lengths of palindromic subsequences. But this method can use a lot of space. The space complexity is O(n^2), where n is the length of the input string. To save space, we can use a better approach that cuts the space needed down to O(n).
Space Optimization Technique
We notice that to find the value of dp[i][j] (the length
of the longest palindromic subsequence from index i to
j), we only need values from the row before
(dp[i+1][...]) and the current row
(dp[i][...]). Because of this, we only need two arrays. We
do not need a full matrix.
Implementation
Here is the optimized code in Python:
def longest_palindromic_subsequence(s: str) -> int:
n = len(s)
# Create two arrays to store results of the current and previous row
prev = [0] * n
curr = [0] * n
for i in range(n - 1, -1, -1):
curr[i] = 1 # Single character is a palindrome of length 1
for j in range(i + 1, n):
if s[i] == s[j]:
curr[j] = 2 + prev[j - 1] if j > i + 1 else 2
else:
curr[j] = max(prev[j], curr[j - 1])
prev = curr[:] # Move to the next row
return prev[n - 1]
# Example usage
s = "bbabcbab"
print("Length of Longest Palindromic Subsequence:", longest_palindromic_subsequence(s))Explanation of the Code
- We use two arrays
prevandcurrto keep the lengths of palindromic subsequences. - We go backward through the string. Each character can be a center of a palindromic subsequence.
- For each pair of indices
iandj, we check:- If the characters match (
s[i] == s[j]), we update the current value based on the previous values. - If they do not match, we take the maximum value from previous calculations.
- If the characters match (
- In the end, the length of the longest palindromic subsequence is in
prev[n - 1].
Benefits of This Approach
- Space Complexity: We cut it from O(n^2) to O(n).
- Time Complexity: It stays O(n^2), because we still need to check each substring.
This better way is great for larger inputs where memory use matters. It helps us compute the Longest Palindromic Subsequence without needing a full 2D array. If we want to learn more about dynamic programming, we can check out related articles like Dynamic Programming: Longest Common Subsequence.
Recursive Approach with Memoization for Longest Palindromic Subsequence
We use the recursive approach to find the Longest Palindromic Subsequence. This approach breaks the problem into smaller parts. We want to find the longest palindromic subsequence by looking at characters from both ends of the string.
Recursive Relation
- If the characters at the two ends match (
s[i] == s[j]), we get2 + LPS(s, i+1, j-1). - If they do not match, we use
max(LPS(s, i+1, j), LPS(s, i, j-1)).
Base Case
- If
i > j, we return0. - If
i == j, we return1because a single character is a palindrome.
Memoization
To make the recursive approach faster, we can use memoization. This means we store the results of subproblems. This way, we do not have to calculate the same thing again. We can use a 2D array for this.
Implementation in Python
def longest_palindromic_subsequence(s: str) -> int:
n = len(s)
memo = [[-1 for _ in range(n)] for _ in range(n)]
def lps(i: int, j: int) -> int:
if i > j:
return 0
if i == j:
return 1
if memo[i][j] != -1:
return memo[i][j]
if s[i] == s[j]:
memo[i][j] = 2 + lps(i + 1, j - 1)
else:
memo[i][j] = max(lps(i + 1, j), lps(i, j - 1))
return memo[i][j]
return lps(0, n - 1)
# Example usage
s = "bbbab"
print(longest_palindromic_subsequence(s)) # Output: 4This code works well to find the length of the longest palindromic subsequence. The time it takes is O(n^2) and the space we use is also O(n^2) because of the memoization table. The recursive way helps us understand the problem better before we try harder dynamic programming solutions.
For more learning on dynamic programming, we can look at other problems like Dynamic Programming - Longest Common Subsequence or Dynamic Programming - Fibonacci Number with Memoization.
Iterative Approach for Longest Palindromic Subsequence
We can solve the Longest Palindromic Subsequence (LPS) problem with an iterative approach. This method uses dynamic programming to create a solution step by step. We build a 2D table. Each entry ( dp[i][j] ) shows the length of the longest palindromic subsequence in the substring from index ( i ) to index ( j ).
Algorithm Steps:
- Initialization:
- We create a 2D array
dpof size ( n n ) (where ( n ) is the length of the input string) and set it to 0. - Each character is a palindrome of length 1. So we set ( dp[i][i] = 1 ) for all ( i ).
- We create a 2D array
- Filling the Table:
- We look at substring lengths from 2 to ( n ):
- For each starting index ( i ):
- We calculate the ending index ( j = i + length - 1 ).
- If the characters at ( i ) and ( j ) are the same, we update (
dp[i][j] ):
- ( dp[i][j] = dp[i+1][j-1] + 2 )
- If they are not the same, we take the maximum from the two possible
palindromic subsequences:
- ( dp[i][j] = (dp[i+1][j], dp[i][j-1]) )
- For each starting index ( i ):
- We look at substring lengths from 2 to ( n ):
- Result:
- We find the length of the longest palindromic subsequence at ( dp[0][n-1] ).
Example Implementation in Java:
public class LongestPalindromicSubsequence {
public int longestPalindromeSubseq(String s) {
int n = s.length();
int[][] dp = new int[n][n];
// Every single character is a palindrome of length 1
for (int i = 0; i < n; i++) {
dp[i][i] = 1;
}
// Build the table in a bottom-up manner
for (int length = 2; length <= n; length++) {
for (int i = 0; i < n - length + 1; i++) {
int j = i + length - 1;
if (s.charAt(i) == s.charAt(j)) {
dp[i][j] = dp[i + 1][j - 1] + 2;
} else {
dp[i][j] = Math.max(dp[i + 1][j], dp[i][j - 1]);
}
}
}
return dp[0][n - 1];
}
}Example Implementation in Python:
class Solution:
def longestPalindromeSubseq(self, s: str) -> int:
n = len(s)
dp = [[0] * n for _ in range(n)]
# Every single character is a palindrome of length 1
for i in range(n):
dp[i][i] = 1
# Build the table in a bottom-up manner
for length in range(2, n + 1):
for i in range(n - length + 1):
j = i + length - 1
if s[i] == s[j]:
dp[i][j] = dp[i + 1][j - 1] + 2
else:
dp[i][j] = max(dp[i + 1][j], dp[i][j - 1])
return dp[0][n - 1]Example Implementation in C++:
class Solution {
public:
int longestPalindromeSubseq(string s) {
int n = s.size();
vector<vector<int>> dp(n, vector<int>(n, 0));
// Every single character is a palindrome of length 1
for (int i = 0; i < n; i++) {
dp[i][i] = 1;
}
// Build the table in a bottom-up manner
for (int length = 2; length <= n; length++) {
for (int i = 0; i < n - length + 1; i++) {
int j = i + length - 1;
if (s[i] == s[j]) {
dp[i][j] = dp[i + 1][j - 1] + 2;
} else {
dp[i][j] = max(dp[i + 1][j], dp[i][j - 1]);
}
}
}
return dp[0][n - 1];
}
};We use this iterative approach to find the longest palindromic subsequence in ( O(n^2) ) time and with ( O(n^2) ) space. For better space use, we can look at techniques that help reduce space in the next sections.
Comparing Different Approaches for Longest Palindromic Subsequence
When we solve the Longest Palindromic Subsequence (LPS) problem, we can use different methods. Each method has its good and bad sides. Here we look at three common ways: Dynamic Programming, Recursive with Memoization, and Iterative.
Dynamic Programming Approach
The Dynamic Programming (DP) method gives us a quick solution. It has a time complexity of O(n^2) and a space complexity of O(n^2). The main idea is to create a DP table. In this table, dp[i][j] shows the length of the longest palindromic subsequence in the substring from index i to j.
public int longestPalindromicSubseq(String s) {
int n = s.length();
int[][] dp = new int[n][n];
for (int i = 0; i < n; i++) {
dp[i][i] = 1; // Each character is a palindrome of length 1
}
for (int length = 2; length <= n; length++) {
for (int i = 0; i < n - length + 1; i++) {
int j = i + length - 1;
if (s.charAt(i) == s.charAt(j)) {
dp[i][j] = dp[i + 1][j - 1] + 2; // Characters match
} else {
dp[i][j] = Math.max(dp[i + 1][j], dp[i][j - 1]); // Characters do not match
}
}
}
return dp[0][n - 1];
}Recursive Approach with Memoization
We can also use recursion and improve it with memoization. This way, we do not calculate the same things again. The time complexity stays O(n^2), but the space complexity gets better to O(n) because of the memoization array.
def longestPalindromicSubseq(s: str) -> int:
memo = {}
def recurse(i, j):
if i > j:
return 0
if i == j:
return 1
if (i, j) in memo:
return memo[(i, j)]
if s[i] == s[j]:
memo[(i, j)] = 2 + recurse(i + 1, j - 1)
else:
memo[(i, j)] = max(recurse(i + 1, j), recurse(i, j - 1))
return memo[(i, j)]
return recurse(0, len(s) - 1)Iterative Approach
We can also do an iterative approach. This way, we use just one array to keep the intermediate results. This helps to lower the space complexity to O(n).
int longestPalindromicSubseq(string s) {
int n = s.size();
vector<int> dp(n, 0);
for (int i = n - 1; i >= 0; i--) {
int prev = 0;
for (int j = i; j < n; j++) {
int temp = dp[j];
if (i == j) {
dp[j] = 1; // Single character
} else if (s[i] == s[j]) {
dp[j] = prev + 2; // Characters match
} else {
dp[j] = max(dp[j], dp[j - 1]); // Characters do not match
}
prev = temp;
}
}
return dp[n - 1];
}Summary of Approaches
- Dynamic Programming: Best for big strings with O(n^2) time and O(n^2) space.
- Recursive with Memoization: Easy to understand but still O(n^2) time and O(n) space.
- Iterative: Good space use with O(n^2) time and O(n) space.
We choose the best way based on the problem needs. This includes string size and memory we have. For more about dynamic programming methods, we can check Dynamic Programming - Longest Common Subsequence.
Frequently Asked Questions
1. What is the Longest Palindromic Subsequence?
The Longest Palindromic Subsequence problem is about finding the longest subsequence in a string that is a palindrome. A palindrome is a sequence that looks the same backward and forward. For example, in the string “character”, the longest palindromic subsequence is “carac”. It has a length of 5. We need to understand this problem well to learn dynamic programming techniques.
2. How do I solve the Longest Palindromic Subsequence using Dynamic Programming?
To solve the Longest Palindromic Subsequence with dynamic programming, we can make a 2D array. This array stores the lengths of palindromic subsequences for different parts of the string. We fill this table by checking character matches and using values we calculated before. For more details and code examples, you can look at the Dynamic Programming Approach for Longest Palindromic Subsequence in Java.
3. What are the time and space complexities of the Longest Palindromic Subsequence algorithm?
The time complexity of the Longest Palindromic Subsequence algorithm with dynamic programming is O(n^2). Here n is the length of the string we give. The space complexity is also O(n^2). This is because we use a 2D table to save results. But if we use some optimization tricks, we can lower the space complexity to O(n) while keeping the same time complexity.
4. Can I use a recursive approach to solve the Longest Palindromic Subsequence problem?
Yes, we can use a recursive approach with memoization to solve the Longest Palindromic Subsequence problem. This means we check characters one by one and store the results of smaller problems. This way, we avoid counting the same thing again. This method has a time complexity of O(n^2) too. But some programmers find it easier to understand. For more details, check the section on Recursive Approach with Memoization for Longest Palindromic Subsequence.
5. How can I compare different approaches for solving the Longest Palindromic Subsequence?
To compare different ways of solving the Longest Palindromic Subsequence, we should look at time complexity, space complexity, and how easy it is to use. Dynamic programming gives a good balance of speed and efficiency. Meanwhile, recursive methods with memoization can be easier to understand. For more ideas on different methods, visit the section titled Comparing Different Approaches for Longest Palindromic Subsequence.