Skip to content

Commit 0cb2f0c

Browse files
committed
feat(algorithms, dynamic-programming): palindromic substrings
1 parent 72a999a commit 0cb2f0c

28 files changed

+244
-33
lines changed
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 three. 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
89.4 KB
Loading

0 commit comments

Comments
 (0)