Skip to content

Commit 6cb240c

Browse files
authored
Merge pull request #151 from BrianLusina/feat/algorithms-dynamic-programming
feat(algorithms, dynamic programming): dynamic programming problems
2 parents 72a999a + 65dc250 commit 6cb240c

File tree

56 files changed

+389
-36
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

56 files changed

+389
-36
lines changed

DIRECTORY.md

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,12 @@
6969
* [Test Min Distance](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/dynamic_programming/min_distance/test_min_distance.py)
7070
* Min Path Sum
7171
* [Test Min Path Sum](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/dynamic_programming/min_path_sum/test_min_path_sum.py)
72+
* Palindromic Substring
73+
* [Longest Palindromic Substring](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/dynamic_programming/palindromic_substring/longest_palindromic_substring.py)
74+
* [Test Longest Palindromic Substring](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/dynamic_programming/palindromic_substring/test_longest_palindromic_substring.py)
75+
* [Test Palindromic Substring](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/dynamic_programming/palindromic_substring/test_palindromic_substring.py)
76+
* Pascals Triangle
77+
* [Test Pascals Triangle](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/dynamic_programming/pascals_triangle/test_pascals_triangle.py)
7278
* Shortest Common Supersequence
7379
* [Test Shortest Common Supersequence](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/dynamic_programming/shortest_common_supersequence/test_shortest_common_supersequence.py)
7480
* Unique Paths
@@ -131,6 +137,9 @@
131137
* [Test Find Min Arrows](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/greedy/min_arrows/test_find_min_arrows.py)
132138
* Spread Stones
133139
* [Test Minimum Moves To Spread Stones](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/greedy/spread_stones/test_minimum_moves_to_spread_stones.py)
140+
* Heap
141+
* Schedule Tasks
142+
* [Test Schedule Tasks On Minimum Machines](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/heap/schedule_tasks/test_schedule_tasks_on_minimum_machines.py)
134143
* Huffman
135144
* [Decoding](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/huffman/decoding.py)
136145
* [Encoding](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/huffman/encoding.py)
@@ -231,7 +240,6 @@
231240
* Largest Palindrome Product
232241
* [Test Largest Palindrome Product](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/two_pointers/palindrome/largest_palindrome_product/test_largest_palindrome_product.py)
233242
* [Longest Palindrome](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/two_pointers/palindrome/longest_palindrome.py)
234-
* [Longest Palindromic Substring](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/two_pointers/palindrome/longest_palindromic_substring.py)
235243
* [Palindrome Index](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/two_pointers/palindrome/palindrome_index.py)
236244
* [Palindrome Pairs](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/two_pointers/palindrome/palindrome_pairs.py)
237245
* Permutation Palindrome
@@ -804,8 +812,6 @@
804812
* [Test Mini Max Sum](https://github.com/BrianLusina/PythonSnips/blob/master/pymath/mini_max_sum/test_mini_max_sum.py)
805813
* Multiply 5
806814
* [Test Multiply 5](https://github.com/BrianLusina/PythonSnips/blob/master/pymath/multiply_5/test_multiply_5.py)
807-
* Pascals Triangle
808-
* [Test Pascals Triangle](https://github.com/BrianLusina/PythonSnips/blob/master/pymath/pascals_triangle/test_pascals_triangle.py)
809815
* Perfect Square
810816
* [Test Perfect Squares](https://github.com/BrianLusina/PythonSnips/blob/master/pymath/perfect_square/test_perfect_squares.py)
811817
* Rectangle Area
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
# Palindromic Substring
2+
3+
## Palindromic Substring
4+
5+
Given a string, s, return the number of palindromic substrings contained in it. A substring is a contiguous sequence of
6+
characters in a string. A palindrome is a phrase, word, or sequence that reads the same forward and backward.
7+
8+
### Constraints
9+
10+
- 1 <= `s.length` <= 1000
11+
- `s` consists of lower English characters
12+
13+
### Examples
14+
15+
![Example 1](./images/examples/palindromic_substring_example_1.png)
16+
![Example 2](./images/examples/palindromic_substring_example_2.png)
17+
18+
### Solution
19+
20+
If we look at the example above, we notice that any substring of length 3 contains a substring of length 1 at the center.
21+
Although we had already checked all substrings of length 1, our algorithm rechecked them for substrings of longer lengths.
22+
This rechecking consumes a lot of time, which can be avoided by storing and reusing the results of the earlier computations.
23+
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]
24+
will store whether the string s[i..j] is a palindromic substring. If the cell dp[i][j] holds the result of the earlier
25+
computation, we will utilize it in O(1) lookup time instead of making a recursive call again.
26+
27+
1. First, we initialize a count variable with 0, which will count the number of palindromic substrings in s.
28+
2. A lookup table is initialized with FALSE.
29+
3. Base case 1: The diagonal in the lookup table is populated with TRUE because any cell in the diagonal corresponds to
30+
a substring of length one, and any string of length one is always a palindrome. The number of one-letter palindromic
31+
strings (i.e., the number of elements in the diagonal) is also added to the count.
32+
4. Base case 2: We check whether all two-letter substrings are palindromes and update the count and dp accordingly. We
33+
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
34+
also increment the count if the value of dp[i][i+1] is TRUE, which tells us that the two-letter substring was a
35+
palindrome.
36+
5. After these base cases, we check all substrings of lengths greater than two. However, we only compare the first and
37+
the last characters. The rest of the string is checked using the lookup table. For example, for a given string “zing”,
38+
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
39+
will tell whether the remaining string “i”, represented by s[1..1], is a palindrome. We’ll take the logical AND of
40+
these two results and store it at dp[0][2] because “zin” is represented by the substring s[0..2]. This way, we’ll
41+
avoid redundant computations and check all possible substrings using the lookup table.
42+
43+
![Solution 1](./images/solutions/palindromic_substring_solution_1.png)
44+
![Solution 2](./images/solutions/palindromic_substring_solution_2.png)
45+
![Solution 3](./images/solutions/palindromic_substring_solution_3.png)
46+
![Solution 4](./images/solutions/palindromic_substring_solution_4.png)
47+
![Solution 5](./images/solutions/palindromic_substring_solution_5.png)
48+
![Solution 6](./images/solutions/palindromic_substring_solution_6.png)
49+
![Solution 7](./images/solutions/palindromic_substring_solution_7.png)
50+
![Solution 8](./images/solutions/palindromic_substring_solution_8.png)
51+
![Solution 9](./images/solutions/palindromic_substring_solution_9.png)
52+
![Solution 10](./images/solutions/palindromic_substring_solution_10.png)
53+
![Solution 11](./images/solutions/palindromic_substring_solution_11.png)
54+
![Solution 12](./images/solutions/palindromic_substring_solution_12.png)
55+
![Solution 13](./images/solutions/palindromic_substring_solution_13.png)
56+
![Solution 14](./images/solutions/palindromic_substring_solution_14.png)
57+
![Solution 15](./images/solutions/palindromic_substring_solution_15.png)
58+
![Solution 16](./images/solutions/palindromic_substring_solution_16.png)
59+
![Solution 17](./images/solutions/palindromic_substring_solution_17.png)
60+
61+
#### Solution Summary
62+
63+
To recap, the solution to this problem can be divided into the following five main parts:
64+
65+
1. We count all one-letter substrings since any one-letter string is always a palindrome. These results are also stored
66+
in a lookup table to be used later.
67+
2. Next, the algorithm checks all two-letter substrings and updates the count and the lookup table accordingly.
68+
3. After these base cases, the algorithm checks all possible substrings of lengths greater than two. However, it only
69+
compares the first and last characters, and the rest of the substring is checked using the lookup table.
70+
4. Whenever a palindromic substring is found, the count and the lookup table are updated accordingly.
71+
5. After checking all possible substrings, the algorithm terminates and returns the count of the palindromic substrings.
72+
73+
#### Complexity Analysis
74+
75+
##### Time Complexity
76+
77+
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
78+
populate the lookup table.
79+
80+
##### Space Complexity
81+
82+
The space complexity of this algorithm is O(n^2), where n is the length of the input string. This is the space occupied
83+
by the lookup table.
84+
85+
---
86+
87+
## Longest Palindromic Substring
88+
89+
Problem Description
90+
91+
Given a string A of size N, find and return the longest palindromic substring in A.
92+
93+
Substring of string A is A[i...j] where 0 <= i <= j < len(A)
94+
95+
Palindrome string:
96+
97+
A string which reads the same backwards. More formally, A is palindrome if reverse(A) = A.
98+
99+
Incase of conflict, return the substring which occurs first ( with the least starting index).
100+
101+
Input Format
102+
First and only argument is a string A.
103+
104+
Output Format
105+
Return a string denoting the longest palindromic substring of string A.
106+
107+
Example Input
108+
A = "aaaabaaa"
109+
110+
Example Output
111+
"aaabaaa"
112+
113+
Example Explanation
114+
We can see that longest palindromic substring is of length 7 and the string is "aaabaaa".
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
def count_palindromic_substrings(s: str) -> int:
2+
"""
3+
Counts the number of palindromic substrings that can be found in the given string
4+
Args:
5+
s(str): string to check for palindromic substrings
6+
Returns:
7+
int: number of palindromic substrings
8+
"""
9+
n = len(s)
10+
dp = [[False] * n for _ in range(n)]
11+
12+
count = 0
13+
# Set all the strings of length 1 to true, these are all palindromes
14+
for i in range(n):
15+
count += 1
16+
dp[i][i] = True
17+
18+
# iterate over pairs i, i+1, checking if they are equal, if they are, then they are palindromes
19+
for i in range(n - 1):
20+
if s[i] == s[i + 1]:
21+
count += 1
22+
dp[i][i + 1] = True
23+
24+
for diff in range(2, n):
25+
for i in range(n - diff):
26+
j = i + diff
27+
if s[i] == s[j] and dp[i + 1][j - 1]:
28+
count += 1
29+
dp[i][j] = True
30+
31+
return count
32+
33+
34+
def count_palindromic_substrings_2(s: str) -> int:
35+
"""
36+
Counts the number of palindromic substrings that can be found in the given string
37+
Args:
38+
s(str): string to check for palindromic substrings
39+
Returns:
40+
int: number of palindromic substrings
41+
"""
42+
n = len(s)
43+
dp = [[False] * n for _ in range(n)]
44+
45+
count = 0
46+
# Set all the strings of length 1 to true, these are all palindromes
47+
for i in range(n):
48+
count += 1
49+
dp[i][i] = True
50+
51+
# iterate over pairs i, i+1, checking if they are equal, if they are, then they are palindromes
52+
for i in range(n - 1):
53+
dp[i][i + 1] = s[i] == s[i + 1]
54+
# A boolean value is added to the count where True means 1 and False means 0
55+
count += dp[i][i + 1]
56+
57+
# Substrings of lengths greater than 2
58+
for length in range(3, n + 1):
59+
i = 0
60+
# Checking every possible substring of any specific length
61+
for j in range(length - 1, n):
62+
dp[i][j] = dp[i + 1][j - 1] and (s[i] == s[j])
63+
count += dp[i][j]
64+
i += 1
65+
66+
return count
65.8 KB
Loading
47 KB
Loading
114 KB
Loading
86.4 KB
Loading
98.1 KB
Loading
113 KB
Loading
112 KB
Loading

0 commit comments

Comments
 (0)