Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions DIRECTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,12 @@
* [Test Min Distance](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/dynamic_programming/min_distance/test_min_distance.py)
* Min Path Sum
* [Test Min Path Sum](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/dynamic_programming/min_path_sum/test_min_path_sum.py)
* Palindromic Substring
* [Longest Palindromic Substring](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/dynamic_programming/palindromic_substring/longest_palindromic_substring.py)
* [Test Longest Palindromic Substring](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/dynamic_programming/palindromic_substring/test_longest_palindromic_substring.py)
* [Test Palindromic Substring](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/dynamic_programming/palindromic_substring/test_palindromic_substring.py)
* Pascals Triangle
* [Test Pascals Triangle](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/dynamic_programming/pascals_triangle/test_pascals_triangle.py)
* Shortest Common Supersequence
* [Test Shortest Common Supersequence](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/dynamic_programming/shortest_common_supersequence/test_shortest_common_supersequence.py)
* Unique Paths
Expand Down Expand Up @@ -231,7 +237,6 @@
* Largest Palindrome Product
* [Test Largest Palindrome Product](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/two_pointers/palindrome/largest_palindrome_product/test_largest_palindrome_product.py)
* [Longest Palindrome](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/two_pointers/palindrome/longest_palindrome.py)
* [Longest Palindromic Substring](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/two_pointers/palindrome/longest_palindromic_substring.py)
* [Palindrome Index](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/two_pointers/palindrome/palindrome_index.py)
* [Palindrome Pairs](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/two_pointers/palindrome/palindrome_pairs.py)
* Permutation Palindrome
Expand Down Expand Up @@ -804,8 +809,6 @@
* [Test Mini Max Sum](https://github.com/BrianLusina/PythonSnips/blob/master/pymath/mini_max_sum/test_mini_max_sum.py)
* Multiply 5
* [Test Multiply 5](https://github.com/BrianLusina/PythonSnips/blob/master/pymath/multiply_5/test_multiply_5.py)
* Pascals Triangle
* [Test Pascals Triangle](https://github.com/BrianLusina/PythonSnips/blob/master/pymath/pascals_triangle/test_pascals_triangle.py)
* Perfect Square
* [Test Perfect Squares](https://github.com/BrianLusina/PythonSnips/blob/master/pymath/perfect_square/test_perfect_squares.py)
* Rectangle Area
Expand Down
114 changes: 114 additions & 0 deletions algorithms/dynamic_programming/palindromic_substring/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
# Palindromic Substring

## Palindromic Substring

Given a string, s, return the number of palindromic substrings contained in it. A substring is a contiguous sequence of
characters in a string. A palindrome is a phrase, word, or sequence that reads the same forward and backward.

### Constraints

- 1 <= `s.length` <= 1000
- `s` consists of lower English characters

### Examples

![Example 1](./images/examples/palindromic_substring_example_1.png)
![Example 2](./images/examples/palindromic_substring_example_2.png)

### Solution

If we look at the example above, we notice that any substring of length 3 contains a substring of length 1 at the center.
Although we had already checked all substrings of length 1, our algorithm rechecked them for substrings of longer lengths.
This rechecking consumes a lot of time, which can be avoided by storing and reusing the results of the earlier computations.
To do this, we can create a lookup table, dp, of size n×n, where n is the length of the input string. Each cell dp[i][j]
will store whether the string s[i..j] is a palindromic substring. If the cell dp[i][j] holds the result of the earlier
computation, we will utilize it in O(1) lookup time instead of making a recursive call again.

1. First, we initialize a count variable with 0, which will count the number of palindromic substrings in s.
2. A lookup table is initialized with FALSE.
3. Base case 1: The diagonal in the lookup table is populated with TRUE because any cell in the diagonal corresponds to
a substring of length one, and any string of length one is always a palindrome. The number of one-letter palindromic
strings (i.e., the number of elements in the diagonal) is also added to the count.
4. Base case 2: We check whether all two-letter substrings are palindromes and update the count and dp accordingly. We
do this by iterating over the string, comparing s[i] and s[i+1], and storing the result at dp[i][i+1]. After that, we
also increment the count if the value of dp[i][i+1] is TRUE, which tells us that the two-letter substring was a
palindrome.
5. After these base cases, we check all substrings of lengths greater than two. However, we only compare the first and
the last characters. The rest of the string is checked using the lookup table. For example, for a given string “zing”,
we want to check whether “zin” is a palindrome. We’ll only compare ‘z’ and ‘n’ and check the value of dp[1][1], which
will tell whether the remaining string “i”, represented by s[1..1], is a palindrome. We’ll take the logical AND of
these two results and store it at dp[0][2] because “zin” is represented by the substring s[0..2]. This way, we’ll
avoid redundant computations and check all possible substrings using the lookup table.

![Solution 1](./images/solutions/palindromic_substring_solution_1.png)
![Solution 2](./images/solutions/palindromic_substring_solution_2.png)
![Solution 3](./images/solutions/palindromic_substring_solution_3.png)
![Solution 4](./images/solutions/palindromic_substring_solution_4.png)
![Solution 5](./images/solutions/palindromic_substring_solution_5.png)
![Solution 6](./images/solutions/palindromic_substring_solution_6.png)
![Solution 7](./images/solutions/palindromic_substring_solution_7.png)
![Solution 8](./images/solutions/palindromic_substring_solution_8.png)
![Solution 9](./images/solutions/palindromic_substring_solution_9.png)
![Solution 10](./images/solutions/palindromic_substring_solution_10.png)
![Solution 11](./images/solutions/palindromic_substring_solution_11.png)
![Solution 12](./images/solutions/palindromic_substring_solution_12.png)
![Solution 13](./images/solutions/palindromic_substring_solution_13.png)
![Solution 14](./images/solutions/palindromic_substring_solution_14.png)
![Solution 15](./images/solutions/palindromic_substring_solution_15.png)
![Solution 16](./images/solutions/palindromic_substring_solution_16.png)
![Solution 17](./images/solutions/palindromic_substring_solution_17.png)

#### Solution Summary

To recap, the solution to this problem can be divided into the following five main parts:

1. We count all one-letter substrings since any one-letter string is always a palindrome. These results are also stored
in a lookup table to be used later.
2. Next, the algorithm checks all two-letter substrings and updates the count and the lookup table accordingly.
3. After these base cases, the algorithm checks all possible substrings of lengths greater than three. However, it only
compares the first and last characters, and the rest of the substring is checked using the lookup table.
Comment on lines +68 to +69
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Minor text inconsistency: "three" should be "two".

The solution summary states "lengths greater than three" but should say "lengths greater than two" to be consistent with the algorithm description. Base cases handle lengths 1 and 2, so the loop checks lengths starting from 3 (i.e., greater than 2).

📝 Suggested fix
-3. After these base cases, the algorithm checks all possible substrings of lengths greater than three. However, it only
+3. After these base cases, the algorithm checks all possible substrings of lengths greater than two. However, it only
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
3. After these base cases, the algorithm checks all possible substrings of lengths greater than three. However, it only
compares the first and last characters, and the rest of the substring is checked using the lookup table.
3. After these base cases, the algorithm checks all possible substrings of lengths greater than two. However, it only
compares the first and last characters, and the rest of the substring is checked using the lookup table.
🤖 Prompt for AI Agents
In @algorithms/dynamic_programming/palindromic_substring/README.md around lines
68 - 69, Update the sentence that currently reads "lengths greater than three"
to "lengths greater than two" in the README explanation so it correctly matches
the algorithm description (base cases for lengths 1 and 2, loop starts at length
3); ensure the surrounding text still reads naturally after this single-word
change.

4. Whenever a palindromic substring is found, the count and the lookup table are updated accordingly.
5. After checking all possible substrings, the algorithm terminates and returns the count of the palindromic substrings.

#### Complexity Analysis

##### Time Complexity

The time complexity of this algorithm is O(n^2), where n is the length of the input string. This is the time required to
populate the lookup table.

##### Space Complexity

The space complexity of this algorithm is O(n^2), where n is the length of the input string. This is the space occupied
by the lookup table.

---

## Longest Palindromic Substring

Problem Description

Given a string A of size N, find and return the longest palindromic substring in A.

Substring of string A is A[i...j] where 0 <= i <= j < len(A)

Palindrome string:

A string which reads the same backwards. More formally, A is palindrome if reverse(A) = A.

Incase of conflict, return the substring which occurs first ( with the least starting index).

Input Format
First and only argument is a string A.

Output Format
Return a string denoting the longest palindromic substring of string A.

Example Input
A = "aaaabaaa"

Example Output
"aaabaaa"

Example Explanation
We can see that longest palindromic substring is of length 7 and the string is "aaabaaa".
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
def count_palindromic_substrings(s: str) -> int:
"""
Counts the number of palindromic substrings that can be found in the given string
Args:
s(str): string to check for palindromic substrings
Returns:
int: number of palindromic substrings
"""
n = len(s)
dp = [[False] * n for _ in range(n)]

count = 0
# Set all the strings of length 1 to true, these are all palindromes
for i in range(n):
count += 1
dp[i][i] = True

# iterate over pairs i, i+1, checking if they are equal, if they are, then they are palindromes
for i in range(n - 1):
if s[i] == s[i + 1]:
count += 1
dp[i][i + 1] = True

for diff in range(2, n):
for i in range(n - diff):
j = i + diff
if s[i] == s[j] and dp[i + 1][j - 1]:
count += 1
dp[i][j] = True

return count


def count_palindromic_substrings_2(s: str) -> int:
"""
Counts the number of palindromic substrings that can be found in the given string
Args:
s(str): string to check for palindromic substrings
Returns:
int: number of palindromic substrings
"""
n = len(s)
dp = [[False] * n for _ in range(n)]

count = 0
# Set all the strings of length 1 to true, these are all palindromes
for i in range(n):
count += 1
dp[i][i] = True

# iterate over pairs i, i+1, checking if they are equal, if they are, then they are palindromes
for i in range(n - 1):
dp[i][i + 1] = s[i] == s[i + 1]
# A boolean value is added to the count where True means 1 and False means 0
count += dp[i][i + 1]

# Substrings of lengths greater than 2
for length in range(3, n + 1):
i = 0
# Checking every possible substring of any specific length
for j in range(length - 1, n):
dp[i][j] = dp[i + 1][j - 1] and (s[i] == s[j])
count += dp[i][j]
i += 1

return count
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
from . import is_palindrome
from algorithms.two_pointers.palindrome import is_palindrome


def longest_palindromic_substring(phrase: str) -> str:
"""Finds the longest palindromic substring from a given string and returns it. If the string has
"""
Finds the longest palindromic substring from a given string and returns it. If the string has
more than 1 palindromic substring, the first is returned, that is, the first with the lowest index.

Args:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import unittest
from parameterized import parameterized
from algorithms.dynamic_programming.palindromic_substring.longest_palindromic_substring import (
longest_palindromic_substring,
)

LONGEST_PALINDROMIC_SUBSTRING_TEST_CASES = [
("mnm", "mnm"),
("zzz", "zzz"),
("cat", "c"),
("lever", "eve"),
("xyxxyz", "yxxy"),
("wwwwwwwwww", "wwwwwwwwww"),
("tattarrattat", "tattarrattat"),
]
Comment on lines +7 to +15
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Add edge cases for empty string, single character, and length-2 palindromes.

The test suite is missing edge cases that could expose bugs in the implementation:

  • Empty string ""
  • Single character "a"
  • String where the longest palindrome is exactly 2 characters (e.g., "aab""aa")

The last case is particularly important because the implementation in longest_palindromic_substring.py has a bug on line 29: result = [i, i + i] should be result = [i, i + 1]. This bug would cause incorrect results when the longest palindrome is exactly 2 characters, but none of the current test cases would catch it.

🧪 Suggested additional test cases
 LONGEST_PALINDROMIC_SUBSTRING_TEST_CASES = [
+    ("", ""),
+    ("a", "a"),
+    ("aab", "aa"),
+    ("baa", "aa"),
     ("mnm", "mnm"),
     ("zzz", "zzz"),
     ("cat", "c"),
     ("lever", "eve"),
     ("xyxxyz", "yxxy"),
     ("wwwwwwwwww", "wwwwwwwwww"),
     ("tattarrattat", "tattarrattat"),
 ]
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
LONGEST_PALINDROMIC_SUBSTRING_TEST_CASES = [
("mnm", "mnm"),
("zzz", "zzz"),
("cat", "c"),
("lever", "eve"),
("xyxxyz", "yxxy"),
("wwwwwwwwww", "wwwwwwwwww"),
("tattarrattat", "tattarrattat"),
]
LONGEST_PALINDROMIC_SUBSTRING_TEST_CASES = [
("", ""),
("a", "a"),
("aab", "aa"),
("baa", "aa"),
("mnm", "mnm"),
("zzz", "zzz"),
("cat", "c"),
("lever", "eve"),
("xyxxyz", "yxxy"),
("wwwwwwwwww", "wwwwwwwwww"),
("tattarrattat", "tattarrattat"),
]
🤖 Prompt for AI Agents
In
@algorithms/dynamic_programming/palindromic_substring/test_longest_palindromic_substring.py
around lines 7 - 15, Add missing edge-case tests to
LONGEST_PALINDROMIC_SUBSTRING_TEST_CASES by including ("", ""), ("a", "a"), and
a length-2 palindrome case such as ("aab", "aa"); also fix the off-by-one bug in
longest_palindromic_substring.py where the center-expansion result is set with
result = [i, i + i] — change it to result = [i, i + 1] so two-character
palindromes are handled correctly (search for the variable name result and the
incorrect assignment to locate the fix).



class LongestPalindromicSubstringTestCase(unittest.TestCase):
@parameterized.expand(LONGEST_PALINDROMIC_SUBSTRING_TEST_CASES)
def test_longest_palindromic_substrings(self, s: str, expected: str):
actual = longest_palindromic_substring(s)
self.assertEqual(expected, actual)


if __name__ == "__main__":
unittest.main()
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import unittest
from parameterized import parameterized
from algorithms.dynamic_programming.palindromic_substring import (
count_palindromic_substrings,
count_palindromic_substrings_2,
)

COUNT_PALINDROMIC_SUBSTRINGS_TEST_CASES = [
("abc", 3),
("aaa", 6),
("mnm", 4),
("zzz", 6),
("cat", 3),
("lever", 6),
("xyxxyz", 9),
("wwwwwwwwww", 55),
("tattarrattat", 24),
]


class CountPalindromicSubstringsTestCase(unittest.TestCase):
@parameterized.expand(COUNT_PALINDROMIC_SUBSTRINGS_TEST_CASES)
def test_count_palindromic_substrings(self, s: str, expected: int):
actual = count_palindromic_substrings(s)
self.assertEqual(expected, actual)

@parameterized.expand(COUNT_PALINDROMIC_SUBSTRINGS_TEST_CASES)
def test_count_palindromic_substrings_2(self, s: str, expected: int):
actual = count_palindromic_substrings_2(s)
self.assertEqual(expected, actual)


if __name__ == "__main__":
unittest.main()
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ def pascal_nth_row(nth: int) -> List[int]:
We instead use the formula:
NCr = (NCr - 1 * (N - r + 1)) / r where 1 ≤ r ≤ N
as the nth row consists of the following sequence:
NC0, NC1, ......, NCN - 1, NCN
NC0, NC1, ..., NCN - 1, NCN
"""
ncr_1 = 1
row: List[int] = [ncr_1]
Expand Down
30 changes: 0 additions & 30 deletions algorithms/two_pointers/palindrome/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,36 +97,6 @@ This string is already a palindrome, so we print . Removing any one of the chara

---

## Longest Palindromic Substring

Problem Description

Given a string A of size N, find and return the longest palindromic substring in A.

Substring of string A is A[i...j] where 0 <= i <= j < len(A)

Palindrome string:

A string which reads the same backwards. More formally, A is palindrome if reverse(A) = A.

Incase of conflict, return the substring which occurs first ( with the least starting index).

Input Format
First and only argument is a string A.

Output Format
Return a string denoting the longest palindromic substring of string A.

Example Input
A = "aaaabaaa"

Example Output
"aaabaaa"

Example Explanation
We can see that longest palindromic substring is of length 7 and the string is "aaabaaa".


## Palindrome Number

Given an integer, x, return TRUE if it is a palindrome; otherwise, FALSE.
Expand Down
Loading