Skip to content
Merged
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
12 changes: 9 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 @@ -131,6 +137,9 @@
* [Test Find Min Arrows](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/greedy/min_arrows/test_find_min_arrows.py)
* Spread Stones
* [Test Minimum Moves To Spread Stones](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/greedy/spread_stones/test_minimum_moves_to_spread_stones.py)
* Heap
* Schedule Tasks
* [Test Schedule Tasks On Minimum Machines](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/heap/schedule_tasks/test_schedule_tasks_on_minimum_machines.py)
* Huffman
* [Decoding](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/huffman/decoding.py)
* [Encoding](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/huffman/encoding.py)
Expand Down Expand Up @@ -231,7 +240,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 +812,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 two. However, it only
compares the first and last characters, and the rest of the substring is checked using the lookup table.
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,30 @@
import unittest
from parameterized import parameterized
from algorithms.dynamic_programming.palindromic_substring.longest_palindromic_substring import (
longest_palindromic_substring,
)

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"),
]


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
Empty file added algorithms/heap/__init__.py
Empty file.
78 changes: 78 additions & 0 deletions algorithms/heap/schedule_tasks/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# Schedule Tasks on Minimum Machines

We are given an input array, tasks, where tasks[i]=[starti ,endi] represents the start and end times of n tasks. Our
goal is to schedule these tasks on machines given the following criteria:

- A machine can execute only one task at a time.
- A machine can begin executing a new task immediately after completing the previous one.
- An unlimited number of machines are available.

Find the minimum number of machines required to complete these n tasks.

## Constraints

- n == `tasks.length`
- 1 <= `tasks.length` <= 10^3
- 0 <= `tasks[i].start` < `tasks[i].end` <= 10^4

## Examples

![Example 1](images/examples/schedule_tasks_on_minimum_machines_example_1.png)
![Example 2](images/examples/schedule_tasks_on_minimum_machines_example_2.png)
![Example 3](images/examples/schedule_tasks_on_minimum_machines_example_3.png)
![Example 4](images/examples/schedule_tasks_on_minimum_machines_example_4.png)

## Solution

The core intuition for solving this problem is to allocate tasks to the minimum number of machines by reusing machines
whenever possible. The algorithm efficiently manages machine availability by sorting tasks by their start times and using
a min heap to track end times. If the earliest available machine (top of the heap) finishes before or as a task starts,
it is reused and removed from the heap. Otherwise, a new machine is allocated, and the current task’s end time is pushed
into the heap. The heap size at the end represents the minimum number of machines required.

Using the intuition above, we implement the algorithm as follows:

1. Sort the tasks array by the start time of each task to process them in chronological order.
2. Initialize a min heap (machines) to keep track of the end times of tasks currently using machines.
3. Iterate over each task in the sorted tasks array.
- Extract the start and end times of the current task.
- Check if the machine with the earliest finish time is free, i.e., top of machines is less than or equal to the
current task’s start time. If it is, remove it from the heap, as the machine can be reused.
- Push the end time of the current task into the heap, indicating that a machine is now in use until that time.
4. After processing all tasks, return the size of the heap, which represents the minimum number of machines required.

![Solution 1](images/solutions/schedule_tasks_on_minimum_machines_solution_1.png)
![Solution 2](images/solutions/schedule_tasks_on_minimum_machines_solution_2.png)
![Solution 3](images/solutions/schedule_tasks_on_minimum_machines_solution_3.png)
![Solution 4](images/solutions/schedule_tasks_on_minimum_machines_solution_4.png)
![Solution 5](images/solutions/schedule_tasks_on_minimum_machines_solution_5.png)
![Solution 6](images/solutions/schedule_tasks_on_minimum_machines_solution_6.png)
![Solution 7](images/solutions/schedule_tasks_on_minimum_machines_solution_7.png)
![Solution 8](images/solutions/schedule_tasks_on_minimum_machines_solution_8.png)
![Solution 9](images/solutions/schedule_tasks_on_minimum_machines_solution_9.png)
![Solution 10](images/solutions/schedule_tasks_on_minimum_machines_solution_10.png)
![Solution 11](images/solutions/schedule_tasks_on_minimum_machines_solution_11.png)
![Solution 12](images/solutions/schedule_tasks_on_minimum_machines_solution_12.png)
![Solution 13](images/solutions/schedule_tasks_on_minimum_machines_solution_13.png)
![Solution 14](images/solutions/schedule_tasks_on_minimum_machines_solution_14.png)
![Solution 15](images/solutions/schedule_tasks_on_minimum_machines_solution_15.png)
![Solution 16](images/solutions/schedule_tasks_on_minimum_machines_solution_16.png)
![Solution 17](images/solutions/schedule_tasks_on_minimum_machines_solution_17.png)
![Solution 18](images/solutions/schedule_tasks_on_minimum_machines_solution_18.png)
![Solution 19](images/solutions/schedule_tasks_on_minimum_machines_solution_19.png)

### Time Complexity

The time complexity of the above algorithm is O(nlogn), where n is the number of tasks represented by the length of the
tasks array. This is because:

- Sorting the array takes O(nlogn).
- The total cost for heap operations is O(nlogn) because we process n tasks, and each operation on the min-heap has a
time complexity of O(logn).

Therefore, the overall time complexity is O(nlogn).

### Space Complexity

The algorithm’s space complexity is O(n) because the min heap can grow up to a maximum size of n if every task requires
a separate machine.
Loading
Loading