The Longest Palindromic Subsequence (LPS) problem is the problem of finding the longest subsequences of a string that is also a palindrome.

*The problem differs from problem of finding common substrings. Unlike substrings, subsequences are not required to occupy consecutive positions within the original sequences.*

For example, Consider the sequence ABBDCACB

The length of Longest Palindromic Subsequence is 5

The Longest Palindromic Subsequence is BCACB

The idea is to use recursion to solve this problem. The idea is compare the last character of the string X[i..j] with its first character. There are two possibilities –

- If the last character of the string is same as the first character, we include first and last characters in palindrome and recurse for the remaining substring X[i+1, j-1]

- If last character of string is different from the first character, we return maximum of the two values we get by
- removing the last character and recursing for the remaining substring X[i, j-1]
- removing the first character and recursing for the remaining substring X[i+1, j]

This yields the below recursive relation to find the length of longest repeated Subsequence of a sequence X.

| 1 (if i = j)

LPS[i..j] = | LPS[i+1..j-1] + 2 (if X[i] = X[j])

| max (LPS[i+1..j], LPS[i..j-1]) (if X[i] != X[j])

Below solution finds the length of longest repeated Subsequence of sequence X recursively by using above relations.

## C++

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
#include <iostream> #include <string> #include <algorithm> using namespace std; // Function to find the length of Longest Palindromic Subsequence // of substring X[i..j] int longestPalindrome(string X, int i, int j) { // base condition if (i > j) return 0; // if string X has only one character, it is palindrome if (i == j) return 1; // if last character of the string is same as the first character if (X[i] == X[j]) { // include first and last characters in palindrome // and recurse for the remaining substring X[i+1, j-1] return longestPalindrome(X, i + 1, j - 1) + 2; } // if last character of string is different to the first character // 1. Remove last character and recurse for the remaining // substring X[i, j-1] // 2. Remove first character and recurse for the remaining // substring X[i+1, j] // return maximum of the two values return max (longestPalindrome(X, i, j - 1), longestPalindrome(X, i + 1, j)); } // Longest Palindromic Subsequence using Dynamic Programming int main() { string X = "ABBDCACB"; int n = X.length(); cout << "The length of Longest Palindromic Subsequence is " << longestPalindrome(X, 0, n - 1); return 0; } |

`Output:`

The length of Longest Palindromic Subsequence is 5

## Java

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
class LongestPalindrome { // Function to find the length of Longest Palindromic Subsequence // of substring X[i..j] public static int longestPalindrome(String X, int i, int j) { // base condition if (i > j) { return 0; } // if String X has only one character, it is palindrome if (i == j) { return 1; } // if last character of the string is same as the first character if (X.charAt(i) == X.charAt(j)) { // include first and last characters in palindrome // and recurse for the remaining substring X[i+1, j-1] return longestPalindrome(X, i + 1, j - 1) + 2; } // if last character of string is different to the first character // return maximum of - // 1. Remove last character & recurse for the remaining // substring X[i, j-1] // 2. Remove first character & recurse for the remaining // substring X[i+1, j] return Integer.max(longestPalindrome(X, i, j - 1), longestPalindrome(X, i + 1, j)); } // main method public static void main(String[] args) { String X = "ABBDCACB"; int n = X.length(); System.out.print("The length of Longest Palindromic Subsequence is " + longestPalindrome(X, 0, n - 1)); } } |

`Output:`

The length of Longest Palindromic Subsequence is 5

The worst case time complexity of above solution is exponential O(2^{n}) and auxiliary space used by the program is O(1). The worst case happens when there is no repeated character present in X (i.e. LPS length is 1) and each recursive call will end up in two recursive calls.

The LPS problem has an optimal substructure and also exhibits overlapping subproblems. Let us consider recursion tree for sequence of length 6 having all distinct characters (say ABCDEF) whose LPS length is 1.

As we can see, the same sub-problems (highlighted in same color) are getting computed again and again. We know that problems having optimal substructure and overlapping subproblems can be solved by dynamic programming, in which subproblem solutions are *Memo*ized rather than computed again and again. This method is illustrated below in C++ and Java –

## C++

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
#include <iostream> #include <string> #include <unordered_map> using namespace std; // Function to find the length of Longest Palindromic Subsequence // of substring X[i..j] int longestPalindrome(string X, int i, int j, auto &lookup) { // base condition if (i > j) return 0; // if string X has only one character, it is palindrome if (i == j) return 1; // construct a unique map key from dynamic elements of the input string key = to_string(i) + "|" + to_string(j); // if sub-problem is seen for the first time, solve it and // store its result in a map if (lookup.find(key) == lookup.end()) { /* if last character of the string is same as the first character include first and last characters in palindrome and recurse for the remaining substring X[i+1, j-1] */ if (X[i] == X[j]) lookup[key] = longestPalindrome(X, i + 1, j - 1, lookup) + 2; else /* if last character of string is different to the first character 1. Remove last char & recurse for the remaining substring X[i, j-1] 2. Remove first char & recurse for the remaining substring X[i+1, j] 3. Return maximum of the two values */ lookup[key] = max (longestPalindrome(X, i, j - 1, lookup), longestPalindrome(X, i + 1, j, lookup)); } // return the subproblem solution from the map return lookup[key]; } // Longest Palindromic Subsequence using Dynamic Programming int main() { string X = "ABBDCACB"; int n = X.length(); // create a map to store solutions of subproblems unordered_map<string, int> lookup; cout << "The length of Longest Palindromic Subsequence is " << longestPalindrome(X, 0, n - 1, lookup); return 0; } |

`Output:`

The length of Longest Palindromic Subsequence is 5

## Java

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
import java.util.HashMap; import java.util.Map; class LongestPalindrome { // Function to find the length of Longest Palindromic Subsequence // of subString X[i..j] public static int longestPalindrome(String X, int i, int j, Map<String, Integer> lookup) { // base condition if (i > j) { return 0; } // if String X has only one character, it is palindrome if (i == j) { return 1; } // construct a unique map key from dynamic elements of the input String key = i + "|" + j; // if sub-problem is seen for the first time, solve it and // store its result in a map if (!lookup.containsKey(key)) { /* if last character of the string is same as the first character include first and last characters in palindrome and recurse for the remaining subString X[i+1, j-1] */ if (X.charAt(i) == X.charAt(j)) { lookup.put(key, longestPalindrome(X, i + 1, j - 1, lookup) + 2); } else { /* if last character of string is different to the first character 1. Remove last char & recurse for the remaining subString X[i, j-1] 2. Remove first char & recurse for the remaining subString X[i+1, j] 3. Return maximum of the two values */ lookup.put(key, Integer.max(longestPalindrome(X, i, j - 1, lookup), longestPalindrome(X, i + 1, j, lookup))); } } // return the sub-problem solution from the map return lookup.get(key); } // Longest Palindromic Subsequence using Dynamic Programming public static void main(String[] args) { String X = "ABBDCACB"; int n = X.length(); // create a map to store solutions of subproblems Map<String, Integer> lookup = new HashMap<>(); System.out.print("The length of Longest Palindromic Subsequence is " + longestPalindrome(X, 0, n - 1, lookup)); } } |

`Output:`

The length of Longest Palindromic Subsequence is 5

The time complexity of above solution is O(n^{2}) and auxiliary space used by the program is O(n^{2}).

Above solutions only find the length of Longest Palindromic Subsequence but do not print the Longest Palindromic Subsequence itself.

##### How can we print Longest Palindromic Subsequence?

The Longest Palindromic Subsequence problem is classic variation of Longest Common Subsequence (LCS) problem. The idea is to find LCS of given string with its reverse i.e. call LCS(X, reverse(X)) and the Longest Common Subsequence will be Longest Palindromic Subsequence.

## C++

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
#include <iostream> #include <string> #include <algorithm> using namespace std; #define N 25 // lookup[i][j] stores the length of LCS of substring X[0..i-1], Y[0..j-1] int lookup[N][N]; // Function to find LCS of string X[0..m-1] and Y[0..n-1] string longestPalindrome(string X, string Y, int m, int n) { // return empty string if we have reached the end of // either sequence if (m == 0 || n == 0) return string(""); // if last character of X and Y matches if (X[m - 1] == Y[n - 1]) { // append current character (X[m-1] or Y[n-1]) to LCS of // substring X[0..m-2] and Y[0..n-2] return longestPalindrome(X, Y, m - 1, n - 1) + X[m - 1]; } // else when the last character of X and Y are different // if top cell of current cell has more value than the left // cell, then drop current character of string X and find LCS // of substring X[0..m-2], Y[0..n-1] if (lookup[m - 1][n] > lookup[m][n - 1]) return longestPalindrome(X, Y, m - 1, n); // if left cell of current cell has more value than the top // cell, then drop current character of string Y and find LCS // of substring X[0..m-1], Y[0..n-2] return longestPalindrome(X, Y, m, n - 1); } // Function to find length of LCS of substring X[0..n-1] and Y[0..n-1] int LCSLength(string X, string Y, int n) { // first row and first column of the lookup table // are already 0 as lookup[][] is globally declared // fill the lookup table in bottom-up manner for (int i = 1; i <= n; i++) { for (int j = 1; j <= n; j++) { // if current character of X and Y matches if (X[i - 1] == Y[j - 1]) lookup[i][j] = lookup[i - 1][j - 1] + 1; // else if current character of X and Y don't match else lookup[i][j] = max(lookup[i - 1][j], lookup[i][j - 1]); } } return lookup[n][n]; } // Longest Palindromic Subsequence using Dynamic Programming int main() { string X = "ABBDCACB"; int m = X.length(); // string Y is reverse of X string Y = X; reverse(Y.begin(), Y.end()); // Find length of Longest Palindromic Subsequence using LCS cout << "The length of Longest Palindromic Subsequence is " << LCSLength(X, Y, m) << '\n'; // Print Longest Palindromic Subsequence using lookup table cout << "The Longest Palindromic Subsequence is " << longestPalindrome(X, Y, m, m); return 0; } |

## Java

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 |
class LongestPalindrome { // Function to find LCS of String X[0..m-1] and Y[0..n-1] public static String longestPalindrome(String X, String Y, int m, int n, int[][] T) { // return empty string if we have reached the end of // either sequence if (m == 0 || n == 0) { return ""; } // if last character of X and Y matches if (X.charAt(m - 1) == Y.charAt(n - 1)) { // append current character (X[m-1] or Y[n-1]) to LCS of // substring X[0..m-2] and Y[0..n-2] return longestPalindrome(X, Y, m - 1, n - 1, T) + X.charAt(m - 1); } // else when the last character of X and Y are different // if top cell of current cell has more value than the left // cell, then drop current character of String X and find LCS // of substring X[0..m-2], Y[0..n-1] if (T[m - 1][n] > T[m][n - 1]) { return longestPalindrome(X, Y, m - 1, n, T); } // if left cell of current cell has more value than the top // cell, then drop current character of String Y and find LCS // of substring X[0..m-1], Y[0..n-2] return longestPalindrome(X, Y, m, n - 1, T); } // Function to find length of LCS of substring X[0..n-1] and Y[0..n-1] public static int LCSLength(String X, String Y, int n, int[][] T) { // first row and first column of the lookup table // are already 0 as T[][] is globally declared // fill the lookup table in bottom-up manner for (int i = 1; i <= n; i++) { for (int j = 1; j <= n; j++) { // if current character of X and Y matches if (X.charAt(i - 1) == Y.charAt(j - 1)) { T[i][j] = T[i - 1][j - 1] + 1; } // else if current character of X and Y don't match else { T[i][j] = Integer.max(T[i - 1][j], T[i][j - 1]); } } } return T[n][n]; } // main function public static void main(String[] args) { String X = "ABBDCACB"; // String Y is reverse of X String Y = new StringBuilder(X).reverse().toString(); // T[i][j] stores the length of LCS of substring X[0..i-1], Y[0..j-1] int[][] T = new int[X.length() + 1][X.length() + 1]; // Find length of Longest Palindromic Subsequence using LCS System.out.println("The length of Longest Palindromic Subsequence is " + LCSLength(X, Y, X.length(), T)); // Print Longest Palindromic Subsequence using lookup table System.out.println("The Longest Palindromic Subsequence is " + longestPalindrome(X, Y, X.length(), X.length(), T)); } } |

`Output:`

The length of Longest Palindromic Subsequence is 5

The Longest Palindromic Subsequence is BCACB

The time complexity of above solution is O(n^{2}) and auxiliary space used by the program is O(n^{2}).

**Exercise:** Write bottom-up solution for above recursive top-down version.

**Thanks for reading.**

Please use our online compiler to post code in comments. To contribute, get in touch with us.

Like us? Please spread the word and help us grow. Happy coding 🙂

## Leave a Reply

Can you also post the longest palindromic substring solution?

Xenial, please refer below post for longest palindromic substring solution –

https://www.techiedelight.com/longest-palindromic-substring-non-dp-space-optimized-solution/

Thanks. I also wanted to point out that it’d be great if there was numbering with the list of problems here (https://www.techiedelight.com/list-of-problems/)

Thanks for your suggestion. We have added the numbering. Happy coding 🙂

I may be missing something here, but it looks like this solution has a bug or it was run on a different input:

ABBDCACB does not contain the string “BCACB”. The longest palindrome I see is “CAC”, which would make the answer 3.

Hello, thanks for sharing your concerns. If you take another look, this problem is about subsequences, not substrings, which might not occupy consecutive positions. Hope this clear things for you.

Can You also post the application of this type of questions with real life problems..

It will help a lot in visualisation

This does not work for substrings 🙁