Skip to content

Commit 667faa5

Browse files
Added DP and BF solutions for longest palindromic substring.
1 parent 9e67e3a commit 667faa5

File tree

5 files changed

+203
-0
lines changed

5 files changed

+203
-0
lines changed

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,11 @@ Happy to accept any contributions to this in any langugage.
158158
* [Solution Brute Force](./dynamic_programming/LongestPalindromicSubsequence_1.ts)
159159
* [Solution DP Top Down](./dynamic_programming/LongestPalindromicSubsequence_2.ts)
160160
* [Solution DP Bottoms Up](./dynamic_programming/LongestPalindromicSubsequence_3.ts)
161+
10. Find Longest Palindromic Substring
162+
* [Solution Brute Force](./dynamic_programming/LongestPalindromicSubstring_1.ts)
163+
* [Solution BF Recursive](./dynamic_programming/LongestPalindromicSubstring_2.ts)
164+
* [Solution DP Top Down](./dynamic_programming/LongestPalindromicSubstring_4.ts)
165+
* [Solution DP Bottom Up](./dynamic_programming/LongestPalindromicSubstring_3.ts)
161166

162167
# TODO Items (More topics to explore further)
163168
* [All TODO Items](./TODO.md)
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/**
2+
* Problem: Longest Palindromic Substring.
3+
* Given a string, find the longest possible palindromic substring of the string. The
4+
* substring should be of the exact order without skipping any elements in between.
5+
*
6+
* Solution: Solution is based on simple brute force. We create the boundary pairs of
7+
* the substring using a quadratic loop. For each substring boundary conditions, we invoke
8+
* the check for is palindrome which does an iteraction of O(n) to validate if the string
9+
* is palindrome.
10+
*
11+
* Complexity...
12+
* Time: O(n^3) since the elements are iterated in 3 nested levels.
13+
* Space: O(1) since no other additional space is used.
14+
*
15+
* @param input
16+
*/
17+
function findLongestPalindromicSubstring1(input: string) {
18+
let maxLength = 0;
19+
for (let beg = 0; beg < input.length; beg++) {
20+
for(let end = beg; end < input.length; end++) {
21+
if (isPalindrome1(input, beg, end)) {
22+
maxLength = Math.max(maxLength,end - beg + 1);
23+
}
24+
}
25+
}
26+
27+
return maxLength;
28+
}
29+
30+
function isPalindrome1(input: string, beg: number, end: number) {
31+
while(beg !== end) {
32+
if (input[beg] !== input[end]) return false
33+
beg++;
34+
end--;
35+
}
36+
37+
return true;
38+
}
39+
40+
function testLongestPal1(input: string) {
41+
console.log(`Longest Palindromic substring length for input: ${input} is ${findLongestPalindromicSubstring1(input)}`);
42+
}
43+
44+
testLongestPal1("abdbca")
45+
testLongestPal1("cddpd")
46+
testLongestPal1("pqr")
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/**
2+
* Problem: Longest Palindromic Substring.
3+
* Given a string, find the longest possible palindromic substring of the string. The
4+
* substring should be of the exact order without skipping any elements in between.
5+
*
6+
* Solution: Solution is based on simple brute force. We recursively navigate from two ends to squeeze inside. At each step, we check if the
7+
* entire string including the 2 endpoints are palindromes. If the entire string with the 2 ends are not palindromes (which we can compare with
8+
* the lengths equality), we may try to squeeze the endpoints. At each level of squeezing we squeeze eihter left or right.
9+
*
10+
* Complexity...
11+
* Time: O(3^n) since at each character positions, we are expanding the solution space to 3 more childs.
12+
* Space: O(n) since at any point of time, recursion stack is at max n.
13+
*
14+
* @param input
15+
*/
16+
function findLongestPalindromicSubstring2(input: string) {
17+
return findLongestPalSubseqHelper2(input, 0, input.length - 1);
18+
}
19+
20+
function findLongestPalSubseqHelper2(input: string, beg: number, end: number) {
21+
// Boundary/Base conditions to terminate the loop.
22+
if (beg > end) return 0;
23+
if (beg === end) return 1;
24+
25+
// Recursive palindromic checks.
26+
if (input[beg] === input[end]) {
27+
// If the 2 endpoints are equal, we may attempt to recursively check the palindrome.
28+
const maxWithInclusion = 2 + findLongestPalSubseqHelper2(input, beg + 1, end - 1);
29+
30+
// Since, we must include adjacent chars and cannot skip from either side at each level, we should validate
31+
// the length.
32+
if (maxWithInclusion === end - beg + 1) {
33+
return maxWithInclusion;
34+
}
35+
}
36+
37+
// If the 2 ends are not equal, we may try to check substring with beg or end only.
38+
const maxWithExclusion1 = findLongestPalSubseqHelper2(input, beg, end - 1);
39+
const maxWithExclusion2 = findLongestPalSubseqHelper2(input, beg + 1, end);
40+
return Math.max(maxWithExclusion1, maxWithExclusion2);
41+
}
42+
43+
function testLongestPal2(input: string) {
44+
console.log(`Longest Palindromic substring length for input: ${input} is ${findLongestPalindromicSubstring2(input)}`);
45+
}
46+
47+
testLongestPal2("abdbca")
48+
testLongestPal2("cddpd")
49+
testLongestPal2("pqr")
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/**
2+
* Problem: Longest Palindromic Substring.
3+
* Given a string, find the longest possible palindromic substring of the string. The
4+
* substring should be of the exact order without skipping any elements in between.
5+
*
6+
* Solution: DP solution uses the bottom up solution to identify the longest palindromic substring with one
7+
* character at a time. With each new character added, we validate if it makes the entire string palindrome from
8+
* end to end. We do that in O(1) time operation by comparing the ends and comparing the substring excluding the ends.
9+
* On the other side, if the entire string is not palindrome, we check if any of the 2 ends can be skipped to have
10+
* a perfect palindrome. The entire sequence is repeated till we add each character.
11+
*
12+
* Complexity...
13+
* Time: O(n^2) since the elements are iterated in 2 nested levels.
14+
* Space: O(n^2) since entire substring ends combination is saved, althought it can be reduced to O(n).
15+
*
16+
* @param input
17+
*/
18+
function findLongestPalindromicSubstring3(input: string) {
19+
20+
// New DP table for caching.
21+
const dp: number[][] = Array.from({length: input.length + 1}, () => Array(input.length).fill(0));
22+
23+
// Initialize the DP table for boundary conditions
24+
for (let idx = 0; idx < input.length; idx++) dp[idx][idx] = 1;
25+
26+
// Build the entire table
27+
for (let beg = input.length - 1; beg >= 0; beg--) {
28+
for (let end = beg + 1; end < input.length; end++) {
29+
if (input[beg] === input[end] && dp[beg + 1][end - 1] + 2 === end - beg + 1) {
30+
dp[beg][end] = 2 + dp[beg + 1][end - 1];
31+
} else {
32+
dp[beg][end] = Math.max(dp[beg + 1][end], dp[beg][end - 1]);
33+
}
34+
}
35+
}
36+
37+
return dp[0][input.length - 1];
38+
}
39+
40+
function testLongestPal3(input: string) {
41+
console.log(`Longest Palindromic substring length for input: ${input} is ${findLongestPalindromicSubstring3(input)}`);
42+
}
43+
44+
testLongestPal3("abdbca")
45+
testLongestPal3("cddpd")
46+
testLongestPal3("pqr")
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/**
2+
* Problem: Longest Palindromic Substring.
3+
* Given a string, find the longest possible palindromic substring of the string. The
4+
* substring should be of the exact order without skipping any elements in between.
5+
*
6+
* Solution: Solution is based on simple brute force. We recursively navigate from two ends to squeeze inside. At each step, we check if the
7+
* entire string including the 2 endpoints are palindromes. If the entire string with the 2 ends are not palindromes (which we can compare with
8+
* the lengths equality), we may try to squeeze the endpoints. At each level of squeezing we squeeze eihter left or right.
9+
*
10+
* Adds DP table for caching previous saved results.
11+
*
12+
* Complexity...
13+
* Time: O(3^n) since at each character positions, we are expanding the solution space to 3 more childs.
14+
* Space: O(n) since at any point of time, recursion stack is at max n.
15+
*
16+
* @param input
17+
*/
18+
function findLongestPalindromicSubstring4(input: string) {
19+
return findLongestPalSubseqHelper4(input, 0, input.length - 1, Array.from({length: input.length}, () => Array(input.length)));
20+
}
21+
22+
function findLongestPalSubseqHelper4(input: string, beg: number, end: number, dp: number[][]) {
23+
// Boundary/Base conditions to terminate the loop.
24+
if (beg > end) return 0;
25+
if (beg === end) return 1;
26+
27+
// Check if the results are already cached.
28+
if (dp[beg][end] !== undefined) {
29+
return dp[beg][end];
30+
}
31+
32+
// Recursive palindromic checks.
33+
if (input[beg] === input[end]) {
34+
// If the 2 endpoints are equal, we may attempt to recursively check the palindrome.
35+
const maxWithInclusion = 2 + findLongestPalSubseqHelper4(input, beg + 1, end - 1, dp);
36+
37+
// Since, we must include adjacent chars and cannot skip from either side at each level, we should validate
38+
// the length.
39+
if (maxWithInclusion === end - beg + 1) {
40+
return maxWithInclusion;
41+
}
42+
}
43+
44+
// If the 2 ends are not equal, we may try to check substring with beg or end only.
45+
const maxWithExclusion1 = findLongestPalSubseqHelper4(input, beg, end - 1, dp);
46+
const maxWithExclusion2 = findLongestPalSubseqHelper4(input, beg + 1, end, dp);
47+
dp[beg][end] = Math.max(maxWithExclusion1, maxWithExclusion2);
48+
return dp[beg][end]
49+
}
50+
51+
function testLongestPal4(input: string) {
52+
console.log(`Longest Palindromic substring length for input: ${input} is ${findLongestPalindromicSubstring4(input)}`);
53+
}
54+
55+
testLongestPal4("abdbca")
56+
testLongestPal4("cddpd")
57+
testLongestPal4("pqr")

0 commit comments

Comments
 (0)