Skip to content

Commit 9e67e3a

Browse files
Added DP solution for longest palindromic subsequence.
1 parent c031f7f commit 9e67e3a

File tree

3 files changed

+115
-1
lines changed

3 files changed

+115
-1
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,8 @@ Happy to accept any contributions to this in any langugage.
156156
* MaximumRibbonCut_2.ts)
157157
9. Find Longest Palindromic Subsequence
158158
* [Solution Brute Force](./dynamic_programming/LongestPalindromicSubsequence_1.ts)
159-
159+
* [Solution DP Top Down](./dynamic_programming/LongestPalindromicSubsequence_2.ts)
160+
* [Solution DP Bottoms Up](./dynamic_programming/LongestPalindromicSubsequence_3.ts)
160161

161162
# TODO Items (More topics to explore further)
162163
* [All TODO Items](./TODO.md)
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/**
2+
*
3+
* Problem: Given a string of "n" characters, find the longest palindromic subsequence
4+
* of the input string. The subsequnce can remove one or more elements from the original
5+
* string retaining the original order of the string. This is a very different problem
6+
* from finding the longest palindromic substring without the constraint of removing any
7+
* elements.
8+
*
9+
* Use the brute force to generate all possible subsequences. While in a subsequence,
10+
* if the string is already a palindrome from the ends, continue proceeding inwards.
11+
* If it is not palindrome from the end, we can continue ignoring one elements from
12+
* the left and right to see if we can achieve a palindromic subsequence. while the brute
13+
* force exploration happens, we need to cache all the pre-computed results in an
14+
* additional DP storage for use.
15+
*
16+
* Complexity....
17+
* Time: O(n^2) since beg * end is the worst case of computation, rest of the computations
18+
* are cached. Space: O(n^2 + n) since in the worst case, we would have all the n^2 positions
19+
* cahced and stored. Additionally, O(n) space would be used for the recursion stack.
20+
*
21+
* @param input
22+
*/
23+
function findLongestPalSubseq1(input: string) {
24+
return findLongestPalSubseqHelper1(input, 0, input.length - 1);
25+
}
26+
27+
function findLongestPalSubseqHelper1(input: string, beg: number, end: number, dp: number[][] = []): number {
28+
29+
dp[beg] = dp[beg] || [];
30+
31+
if (dp[beg][end] !== undefined)
32+
return dp[beg][end];
33+
34+
if (end < beg) return 0;
35+
else if (end === beg) return 1;
36+
else if (input[beg] === input[end]) {
37+
// Ends are matching, continue inwards.
38+
dp[beg][end] = 2 + findLongestPalSubseqHelper1(input, beg + 1, end - 1);
39+
return dp[beg][end]
40+
}
41+
42+
// If the ends are not matching, we may try to skip an element from the end, to attempt
43+
// to make a palindromic string
44+
const maxWithLeftInclusion = findLongestPalSubseqHelper1(input, beg + 1, end);
45+
const maxWithRightInclusion = findLongestPalSubseqHelper1(input, beg, end - 1);
46+
dp[beg][end] = Math.max(maxWithLeftInclusion, maxWithRightInclusion);
47+
return dp[beg][end];
48+
}
49+
50+
function testLongestPlalindSubseq1(input: string) {
51+
console.log(`Longest Palindromic Subsequence for input ${input}: ${findLongestPalSubseq1(input)}`)
52+
}
53+
54+
testLongestPlalindSubseq1("abdbca")
55+
testLongestPlalindSubseq1("cddpd")
56+
testLongestPlalindSubseq1("pqr")
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/**
2+
*
3+
* Problem: Given a string of "n" characters, find the longest palindromic subsequence
4+
* of the input string. The subsequnce can remove one or more elements from the original
5+
* string retaining the original order of the string. This is a very different problem
6+
* from finding the longest palindromic substring without the constraint of removing any
7+
* elements.
8+
*
9+
* Bottoms up dynamic programming is solution is used for identifying the max subsequence.
10+
* We start by including one character at a time. With each iteration, we include one more
11+
* character. Wuth each new character, there are n^2 possible subsequences, we create those
12+
* possible subsequences. With each subsequence, we identify the max palindromic length
13+
* using earlier computation and preserved results.
14+
*
15+
* Complexity....
16+
* Time: O(n^2) since we are creating all the possible subsequences
17+
* Space: O(n^2) since in the worst case, we would have to store the results of the entire possible combination.
18+
* @param input
19+
*/
20+
function findLongestPalSubseq2(input: string) {
21+
return findLongestPalSubseqHelper2(input);
22+
}
23+
24+
function findLongestPalSubseqHelper2(input: string): number {
25+
26+
const dp: number[][] = Array.from({length: input.length}, () => Array(input.length).fill(0));
27+
for (let idx = 0; idx < input.length; idx++)
28+
dp[idx][idx] = 1;
29+
30+
/**
31+
* DP table is built by including one additional position at each step.
32+
* We start from right to left, in order to use the DP formulation function.
33+
* At each character that is included, we need to consider its pair of subsequences
34+
* with all the other positions/characters identified before.
35+
*/
36+
for (let beg = input.length - 1; beg >= 0; beg--) {
37+
for (let end = beg + 1; end < input.length; end++) {
38+
if (input[beg] === input[end]) {
39+
// If the perfect palindrome is created from the end, we squeeze.
40+
dp[beg][end] = dp[beg + 1][end - 1] + 2;
41+
} else {
42+
// If not, we see the max possible from either left or right.
43+
dp[beg][end] = Math.max(dp[beg + 1][end], dp[beg][end - 1]);
44+
}
45+
}
46+
}
47+
48+
return dp[0][input.length - 1];
49+
}
50+
51+
function testLongestPlalindSubseq2(input: string) {
52+
console.log(`Longest Palindromic Subsequence for input ${input}: ${findLongestPalSubseq2(input)}`)
53+
}
54+
55+
testLongestPlalindSubseq2("abdbca")
56+
testLongestPlalindSubseq2("cddpd")
57+
testLongestPlalindSubseq2("pqr")

0 commit comments

Comments
 (0)