diff --git a/DIRECTORY.md b/DIRECTORY.md index 9c577921..60ece2b8 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -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 @@ -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) @@ -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 @@ -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 diff --git a/algorithms/dynamic_programming/palindromic_substring/README.md b/algorithms/dynamic_programming/palindromic_substring/README.md new file mode 100644 index 00000000..1ac8b51a --- /dev/null +++ b/algorithms/dynamic_programming/palindromic_substring/README.md @@ -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". diff --git a/algorithms/dynamic_programming/palindromic_substring/__init__.py b/algorithms/dynamic_programming/palindromic_substring/__init__.py new file mode 100644 index 00000000..58a3802a --- /dev/null +++ b/algorithms/dynamic_programming/palindromic_substring/__init__.py @@ -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 diff --git a/algorithms/dynamic_programming/palindromic_substring/images/examples/palindromic_substring_example_1.png b/algorithms/dynamic_programming/palindromic_substring/images/examples/palindromic_substring_example_1.png new file mode 100644 index 00000000..364e9ffb Binary files /dev/null and b/algorithms/dynamic_programming/palindromic_substring/images/examples/palindromic_substring_example_1.png differ diff --git a/algorithms/dynamic_programming/palindromic_substring/images/examples/palindromic_substring_example_2.png b/algorithms/dynamic_programming/palindromic_substring/images/examples/palindromic_substring_example_2.png new file mode 100644 index 00000000..442f4666 Binary files /dev/null and b/algorithms/dynamic_programming/palindromic_substring/images/examples/palindromic_substring_example_2.png differ diff --git a/algorithms/dynamic_programming/palindromic_substring/images/solutions/palindromic_substring_solution_1.png b/algorithms/dynamic_programming/palindromic_substring/images/solutions/palindromic_substring_solution_1.png new file mode 100644 index 00000000..a6a26134 Binary files /dev/null and b/algorithms/dynamic_programming/palindromic_substring/images/solutions/palindromic_substring_solution_1.png differ diff --git a/algorithms/dynamic_programming/palindromic_substring/images/solutions/palindromic_substring_solution_10.png b/algorithms/dynamic_programming/palindromic_substring/images/solutions/palindromic_substring_solution_10.png new file mode 100644 index 00000000..fa7729d1 Binary files /dev/null and b/algorithms/dynamic_programming/palindromic_substring/images/solutions/palindromic_substring_solution_10.png differ diff --git a/algorithms/dynamic_programming/palindromic_substring/images/solutions/palindromic_substring_solution_11.png b/algorithms/dynamic_programming/palindromic_substring/images/solutions/palindromic_substring_solution_11.png new file mode 100644 index 00000000..4591e39c Binary files /dev/null and b/algorithms/dynamic_programming/palindromic_substring/images/solutions/palindromic_substring_solution_11.png differ diff --git a/algorithms/dynamic_programming/palindromic_substring/images/solutions/palindromic_substring_solution_12.png b/algorithms/dynamic_programming/palindromic_substring/images/solutions/palindromic_substring_solution_12.png new file mode 100644 index 00000000..541e3efa Binary files /dev/null and b/algorithms/dynamic_programming/palindromic_substring/images/solutions/palindromic_substring_solution_12.png differ diff --git a/algorithms/dynamic_programming/palindromic_substring/images/solutions/palindromic_substring_solution_13.png b/algorithms/dynamic_programming/palindromic_substring/images/solutions/palindromic_substring_solution_13.png new file mode 100644 index 00000000..c44965d5 Binary files /dev/null and b/algorithms/dynamic_programming/palindromic_substring/images/solutions/palindromic_substring_solution_13.png differ diff --git a/algorithms/dynamic_programming/palindromic_substring/images/solutions/palindromic_substring_solution_14.png b/algorithms/dynamic_programming/palindromic_substring/images/solutions/palindromic_substring_solution_14.png new file mode 100644 index 00000000..d2b4be2e Binary files /dev/null and b/algorithms/dynamic_programming/palindromic_substring/images/solutions/palindromic_substring_solution_14.png differ diff --git a/algorithms/dynamic_programming/palindromic_substring/images/solutions/palindromic_substring_solution_15.png b/algorithms/dynamic_programming/palindromic_substring/images/solutions/palindromic_substring_solution_15.png new file mode 100644 index 00000000..81e61fee Binary files /dev/null and b/algorithms/dynamic_programming/palindromic_substring/images/solutions/palindromic_substring_solution_15.png differ diff --git a/algorithms/dynamic_programming/palindromic_substring/images/solutions/palindromic_substring_solution_16.png b/algorithms/dynamic_programming/palindromic_substring/images/solutions/palindromic_substring_solution_16.png new file mode 100644 index 00000000..a30d4169 Binary files /dev/null and b/algorithms/dynamic_programming/palindromic_substring/images/solutions/palindromic_substring_solution_16.png differ diff --git a/algorithms/dynamic_programming/palindromic_substring/images/solutions/palindromic_substring_solution_17.png b/algorithms/dynamic_programming/palindromic_substring/images/solutions/palindromic_substring_solution_17.png new file mode 100644 index 00000000..e5d3ac6e Binary files /dev/null and b/algorithms/dynamic_programming/palindromic_substring/images/solutions/palindromic_substring_solution_17.png differ diff --git a/algorithms/dynamic_programming/palindromic_substring/images/solutions/palindromic_substring_solution_2.png b/algorithms/dynamic_programming/palindromic_substring/images/solutions/palindromic_substring_solution_2.png new file mode 100644 index 00000000..3dd7f9d5 Binary files /dev/null and b/algorithms/dynamic_programming/palindromic_substring/images/solutions/palindromic_substring_solution_2.png differ diff --git a/algorithms/dynamic_programming/palindromic_substring/images/solutions/palindromic_substring_solution_3.png b/algorithms/dynamic_programming/palindromic_substring/images/solutions/palindromic_substring_solution_3.png new file mode 100644 index 00000000..0d07b434 Binary files /dev/null and b/algorithms/dynamic_programming/palindromic_substring/images/solutions/palindromic_substring_solution_3.png differ diff --git a/algorithms/dynamic_programming/palindromic_substring/images/solutions/palindromic_substring_solution_4.png b/algorithms/dynamic_programming/palindromic_substring/images/solutions/palindromic_substring_solution_4.png new file mode 100644 index 00000000..a40bc5bb Binary files /dev/null and b/algorithms/dynamic_programming/palindromic_substring/images/solutions/palindromic_substring_solution_4.png differ diff --git a/algorithms/dynamic_programming/palindromic_substring/images/solutions/palindromic_substring_solution_5.png b/algorithms/dynamic_programming/palindromic_substring/images/solutions/palindromic_substring_solution_5.png new file mode 100644 index 00000000..1c86eb45 Binary files /dev/null and b/algorithms/dynamic_programming/palindromic_substring/images/solutions/palindromic_substring_solution_5.png differ diff --git a/algorithms/dynamic_programming/palindromic_substring/images/solutions/palindromic_substring_solution_6.png b/algorithms/dynamic_programming/palindromic_substring/images/solutions/palindromic_substring_solution_6.png new file mode 100644 index 00000000..258c1cb7 Binary files /dev/null and b/algorithms/dynamic_programming/palindromic_substring/images/solutions/palindromic_substring_solution_6.png differ diff --git a/algorithms/dynamic_programming/palindromic_substring/images/solutions/palindromic_substring_solution_7.png b/algorithms/dynamic_programming/palindromic_substring/images/solutions/palindromic_substring_solution_7.png new file mode 100644 index 00000000..ffc7568d Binary files /dev/null and b/algorithms/dynamic_programming/palindromic_substring/images/solutions/palindromic_substring_solution_7.png differ diff --git a/algorithms/dynamic_programming/palindromic_substring/images/solutions/palindromic_substring_solution_8.png b/algorithms/dynamic_programming/palindromic_substring/images/solutions/palindromic_substring_solution_8.png new file mode 100644 index 00000000..36ed6a78 Binary files /dev/null and b/algorithms/dynamic_programming/palindromic_substring/images/solutions/palindromic_substring_solution_8.png differ diff --git a/algorithms/dynamic_programming/palindromic_substring/images/solutions/palindromic_substring_solution_9.png b/algorithms/dynamic_programming/palindromic_substring/images/solutions/palindromic_substring_solution_9.png new file mode 100644 index 00000000..fea4ef7a Binary files /dev/null and b/algorithms/dynamic_programming/palindromic_substring/images/solutions/palindromic_substring_solution_9.png differ diff --git a/algorithms/two_pointers/palindrome/longest_palindromic_substring.py b/algorithms/dynamic_programming/palindromic_substring/longest_palindromic_substring.py similarity index 87% rename from algorithms/two_pointers/palindrome/longest_palindromic_substring.py rename to algorithms/dynamic_programming/palindromic_substring/longest_palindromic_substring.py index 9fddd371..2dd1e41a 100644 --- a/algorithms/two_pointers/palindrome/longest_palindromic_substring.py +++ b/algorithms/dynamic_programming/palindromic_substring/longest_palindromic_substring.py @@ -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: diff --git a/algorithms/dynamic_programming/palindromic_substring/test_longest_palindromic_substring.py b/algorithms/dynamic_programming/palindromic_substring/test_longest_palindromic_substring.py new file mode 100644 index 00000000..989ca9fc --- /dev/null +++ b/algorithms/dynamic_programming/palindromic_substring/test_longest_palindromic_substring.py @@ -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() diff --git a/algorithms/dynamic_programming/palindromic_substring/test_palindromic_substring.py b/algorithms/dynamic_programming/palindromic_substring/test_palindromic_substring.py new file mode 100644 index 00000000..ad79e807 --- /dev/null +++ b/algorithms/dynamic_programming/palindromic_substring/test_palindromic_substring.py @@ -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() diff --git a/pymath/pascals_triangle/README.md b/algorithms/dynamic_programming/pascals_triangle/README.md similarity index 100% rename from pymath/pascals_triangle/README.md rename to algorithms/dynamic_programming/pascals_triangle/README.md diff --git a/pymath/pascals_triangle/__init__.py b/algorithms/dynamic_programming/pascals_triangle/__init__.py similarity index 96% rename from pymath/pascals_triangle/__init__.py rename to algorithms/dynamic_programming/pascals_triangle/__init__.py index 3a8a70c8..f4769494 100644 --- a/pymath/pascals_triangle/__init__.py +++ b/algorithms/dynamic_programming/pascals_triangle/__init__.py @@ -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] diff --git a/pymath/pascals_triangle/test_pascals_triangle.py b/algorithms/dynamic_programming/pascals_triangle/test_pascals_triangle.py similarity index 100% rename from pymath/pascals_triangle/test_pascals_triangle.py rename to algorithms/dynamic_programming/pascals_triangle/test_pascals_triangle.py diff --git a/algorithms/heap/__init__.py b/algorithms/heap/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/algorithms/heap/schedule_tasks/README.md b/algorithms/heap/schedule_tasks/README.md new file mode 100644 index 00000000..24daf5e4 --- /dev/null +++ b/algorithms/heap/schedule_tasks/README.md @@ -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. diff --git a/algorithms/heap/schedule_tasks/__init__.py b/algorithms/heap/schedule_tasks/__init__.py new file mode 100644 index 00000000..d84a1c15 --- /dev/null +++ b/algorithms/heap/schedule_tasks/__init__.py @@ -0,0 +1,23 @@ +from typing import List +from heapq import heappush, heappop, heapify + + +def minimum_machines(tasks: List[List[int]]) -> int: + # sort tasks by their start time in place, Time cost is O(n log(n)) with space cost being O(n) due to timsort + tasks.sort(key=lambda x: x[0]) + + # initialize a min heap to keep track of the end times + machines: List[int] = [] + heapify(machines) + + for current_task in tasks: + task_start, task_end = current_task + # check if a machine with the earliest finish time is free + if machines and machines[0] <= task_start: + # reuse machine + heappop(machines) + + # assign a machine to the current task + heappush(machines, task_end) + # Return the size of the heap representing the minimum number of machines required + return len(machines) diff --git a/algorithms/heap/schedule_tasks/images/examples/schedule_tasks_on_minimum_machines_example_1.png b/algorithms/heap/schedule_tasks/images/examples/schedule_tasks_on_minimum_machines_example_1.png new file mode 100644 index 00000000..fb416115 Binary files /dev/null and b/algorithms/heap/schedule_tasks/images/examples/schedule_tasks_on_minimum_machines_example_1.png differ diff --git a/algorithms/heap/schedule_tasks/images/examples/schedule_tasks_on_minimum_machines_example_2.png b/algorithms/heap/schedule_tasks/images/examples/schedule_tasks_on_minimum_machines_example_2.png new file mode 100644 index 00000000..6be04580 Binary files /dev/null and b/algorithms/heap/schedule_tasks/images/examples/schedule_tasks_on_minimum_machines_example_2.png differ diff --git a/algorithms/heap/schedule_tasks/images/examples/schedule_tasks_on_minimum_machines_example_3.png b/algorithms/heap/schedule_tasks/images/examples/schedule_tasks_on_minimum_machines_example_3.png new file mode 100644 index 00000000..bd985382 Binary files /dev/null and b/algorithms/heap/schedule_tasks/images/examples/schedule_tasks_on_minimum_machines_example_3.png differ diff --git a/algorithms/heap/schedule_tasks/images/examples/schedule_tasks_on_minimum_machines_example_4.png b/algorithms/heap/schedule_tasks/images/examples/schedule_tasks_on_minimum_machines_example_4.png new file mode 100644 index 00000000..6aebf8fd Binary files /dev/null and b/algorithms/heap/schedule_tasks/images/examples/schedule_tasks_on_minimum_machines_example_4.png differ diff --git a/algorithms/heap/schedule_tasks/images/solutions/schedule_tasks_on_minimum_machines_solution_1.png b/algorithms/heap/schedule_tasks/images/solutions/schedule_tasks_on_minimum_machines_solution_1.png new file mode 100644 index 00000000..1b32189a Binary files /dev/null and b/algorithms/heap/schedule_tasks/images/solutions/schedule_tasks_on_minimum_machines_solution_1.png differ diff --git a/algorithms/heap/schedule_tasks/images/solutions/schedule_tasks_on_minimum_machines_solution_10.png b/algorithms/heap/schedule_tasks/images/solutions/schedule_tasks_on_minimum_machines_solution_10.png new file mode 100644 index 00000000..d82822e4 Binary files /dev/null and b/algorithms/heap/schedule_tasks/images/solutions/schedule_tasks_on_minimum_machines_solution_10.png differ diff --git a/algorithms/heap/schedule_tasks/images/solutions/schedule_tasks_on_minimum_machines_solution_11.png b/algorithms/heap/schedule_tasks/images/solutions/schedule_tasks_on_minimum_machines_solution_11.png new file mode 100644 index 00000000..492ec518 Binary files /dev/null and b/algorithms/heap/schedule_tasks/images/solutions/schedule_tasks_on_minimum_machines_solution_11.png differ diff --git a/algorithms/heap/schedule_tasks/images/solutions/schedule_tasks_on_minimum_machines_solution_12.png b/algorithms/heap/schedule_tasks/images/solutions/schedule_tasks_on_minimum_machines_solution_12.png new file mode 100644 index 00000000..d85d980e Binary files /dev/null and b/algorithms/heap/schedule_tasks/images/solutions/schedule_tasks_on_minimum_machines_solution_12.png differ diff --git a/algorithms/heap/schedule_tasks/images/solutions/schedule_tasks_on_minimum_machines_solution_13.png b/algorithms/heap/schedule_tasks/images/solutions/schedule_tasks_on_minimum_machines_solution_13.png new file mode 100644 index 00000000..0e17c20e Binary files /dev/null and b/algorithms/heap/schedule_tasks/images/solutions/schedule_tasks_on_minimum_machines_solution_13.png differ diff --git a/algorithms/heap/schedule_tasks/images/solutions/schedule_tasks_on_minimum_machines_solution_14.png b/algorithms/heap/schedule_tasks/images/solutions/schedule_tasks_on_minimum_machines_solution_14.png new file mode 100644 index 00000000..f1b824e1 Binary files /dev/null and b/algorithms/heap/schedule_tasks/images/solutions/schedule_tasks_on_minimum_machines_solution_14.png differ diff --git a/algorithms/heap/schedule_tasks/images/solutions/schedule_tasks_on_minimum_machines_solution_15.png b/algorithms/heap/schedule_tasks/images/solutions/schedule_tasks_on_minimum_machines_solution_15.png new file mode 100644 index 00000000..ed78b6ee Binary files /dev/null and b/algorithms/heap/schedule_tasks/images/solutions/schedule_tasks_on_minimum_machines_solution_15.png differ diff --git a/algorithms/heap/schedule_tasks/images/solutions/schedule_tasks_on_minimum_machines_solution_16.png b/algorithms/heap/schedule_tasks/images/solutions/schedule_tasks_on_minimum_machines_solution_16.png new file mode 100644 index 00000000..d158c125 Binary files /dev/null and b/algorithms/heap/schedule_tasks/images/solutions/schedule_tasks_on_minimum_machines_solution_16.png differ diff --git a/algorithms/heap/schedule_tasks/images/solutions/schedule_tasks_on_minimum_machines_solution_17.png b/algorithms/heap/schedule_tasks/images/solutions/schedule_tasks_on_minimum_machines_solution_17.png new file mode 100644 index 00000000..b81698f1 Binary files /dev/null and b/algorithms/heap/schedule_tasks/images/solutions/schedule_tasks_on_minimum_machines_solution_17.png differ diff --git a/algorithms/heap/schedule_tasks/images/solutions/schedule_tasks_on_minimum_machines_solution_18.png b/algorithms/heap/schedule_tasks/images/solutions/schedule_tasks_on_minimum_machines_solution_18.png new file mode 100644 index 00000000..ade2c11b Binary files /dev/null and b/algorithms/heap/schedule_tasks/images/solutions/schedule_tasks_on_minimum_machines_solution_18.png differ diff --git a/algorithms/heap/schedule_tasks/images/solutions/schedule_tasks_on_minimum_machines_solution_19.png b/algorithms/heap/schedule_tasks/images/solutions/schedule_tasks_on_minimum_machines_solution_19.png new file mode 100644 index 00000000..bac5970e Binary files /dev/null and b/algorithms/heap/schedule_tasks/images/solutions/schedule_tasks_on_minimum_machines_solution_19.png differ diff --git a/algorithms/heap/schedule_tasks/images/solutions/schedule_tasks_on_minimum_machines_solution_2.png b/algorithms/heap/schedule_tasks/images/solutions/schedule_tasks_on_minimum_machines_solution_2.png new file mode 100644 index 00000000..1a3df6bf Binary files /dev/null and b/algorithms/heap/schedule_tasks/images/solutions/schedule_tasks_on_minimum_machines_solution_2.png differ diff --git a/algorithms/heap/schedule_tasks/images/solutions/schedule_tasks_on_minimum_machines_solution_3.png b/algorithms/heap/schedule_tasks/images/solutions/schedule_tasks_on_minimum_machines_solution_3.png new file mode 100644 index 00000000..4fc63d1c Binary files /dev/null and b/algorithms/heap/schedule_tasks/images/solutions/schedule_tasks_on_minimum_machines_solution_3.png differ diff --git a/algorithms/heap/schedule_tasks/images/solutions/schedule_tasks_on_minimum_machines_solution_4.png b/algorithms/heap/schedule_tasks/images/solutions/schedule_tasks_on_minimum_machines_solution_4.png new file mode 100644 index 00000000..d14cc2dd Binary files /dev/null and b/algorithms/heap/schedule_tasks/images/solutions/schedule_tasks_on_minimum_machines_solution_4.png differ diff --git a/algorithms/heap/schedule_tasks/images/solutions/schedule_tasks_on_minimum_machines_solution_5.png b/algorithms/heap/schedule_tasks/images/solutions/schedule_tasks_on_minimum_machines_solution_5.png new file mode 100644 index 00000000..af647236 Binary files /dev/null and b/algorithms/heap/schedule_tasks/images/solutions/schedule_tasks_on_minimum_machines_solution_5.png differ diff --git a/algorithms/heap/schedule_tasks/images/solutions/schedule_tasks_on_minimum_machines_solution_6.png b/algorithms/heap/schedule_tasks/images/solutions/schedule_tasks_on_minimum_machines_solution_6.png new file mode 100644 index 00000000..b1d6b4f4 Binary files /dev/null and b/algorithms/heap/schedule_tasks/images/solutions/schedule_tasks_on_minimum_machines_solution_6.png differ diff --git a/algorithms/heap/schedule_tasks/images/solutions/schedule_tasks_on_minimum_machines_solution_7.png b/algorithms/heap/schedule_tasks/images/solutions/schedule_tasks_on_minimum_machines_solution_7.png new file mode 100644 index 00000000..e3371b09 Binary files /dev/null and b/algorithms/heap/schedule_tasks/images/solutions/schedule_tasks_on_minimum_machines_solution_7.png differ diff --git a/algorithms/heap/schedule_tasks/images/solutions/schedule_tasks_on_minimum_machines_solution_8.png b/algorithms/heap/schedule_tasks/images/solutions/schedule_tasks_on_minimum_machines_solution_8.png new file mode 100644 index 00000000..c7c8d2a7 Binary files /dev/null and b/algorithms/heap/schedule_tasks/images/solutions/schedule_tasks_on_minimum_machines_solution_8.png differ diff --git a/algorithms/heap/schedule_tasks/images/solutions/schedule_tasks_on_minimum_machines_solution_9.png b/algorithms/heap/schedule_tasks/images/solutions/schedule_tasks_on_minimum_machines_solution_9.png new file mode 100644 index 00000000..bab9f9fa Binary files /dev/null and b/algorithms/heap/schedule_tasks/images/solutions/schedule_tasks_on_minimum_machines_solution_9.png differ diff --git a/algorithms/heap/schedule_tasks/test_schedule_tasks_on_minimum_machines.py b/algorithms/heap/schedule_tasks/test_schedule_tasks_on_minimum_machines.py new file mode 100644 index 00000000..07bdc218 --- /dev/null +++ b/algorithms/heap/schedule_tasks/test_schedule_tasks_on_minimum_machines.py @@ -0,0 +1,31 @@ +import unittest +from typing import List +from parameterized import parameterized +from algorithms.heap.schedule_tasks import minimum_machines + +SCHEDULE_TASKS_ON_MINIMUM_MACHINES = [ + ([[1, 7], [8, 13], [5, 6], [10, 14], [6, 7]], 2), + ([[1, 7], [8, 9], [3, 6], [9, 14], [6, 7]], 2), + ([[2, 5], [2, 5], [2, 5], [2, 5]], 4), + ([[2, 3], [4, 7], [8, 18], [19, 25], [26, 30]], 1), + ([[12, 13], [13, 15], [17, 20], [13, 14], [19, 21], [18, 20]], 3), + ([[1, 4], [3, 5], [6, 8], [7, 8]], 2), + ([[1, 7], [1, 7], [1, 7], [1, 7], [1, 7], [1, 7]], 6), + ([[1, 3], [3, 5], [5, 9], [9, 12], [12, 13], [13, 16], [16, 17]], 1), + ([[1, 3], [2, 6], [5, 9], [4, 7], [8, 10], [8, 10], [12, 15]], 3), + ([[5, 6], [5, 6], [5, 6], [5, 6], [6, 7]], 4), + ([[5, 6], [6, 7], [1, 7], [10, 14], [8, 13]], 2), +] + + +class ScheduleTasksOnMinimumMachinesTestCase(unittest.TestCase): + @parameterized.expand(SCHEDULE_TASKS_ON_MINIMUM_MACHINES) + def test_schedule_tasks_on_minimum_machines( + self, tasks: List[List[int]], expected: int + ): + actual = minimum_machines(tasks) + self.assertEqual(expected, actual) + + +if __name__ == "__main__": + unittest.main() diff --git a/algorithms/two_pointers/palindrome/README.md b/algorithms/two_pointers/palindrome/README.md index c3d76fac..ee04f954 100755 --- a/algorithms/two_pointers/palindrome/README.md +++ b/algorithms/two_pointers/palindrome/README.md @@ -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.