diff --git a/DIRECTORY.md b/DIRECTORY.md index 5a1e67f8..c82d449d 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -141,6 +141,8 @@ * [Test Gas Stations](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/greedy/gas_stations/test_gas_stations.py) * Jump Game * [Test Jump Game](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/greedy/jump_game/test_jump_game.py) + * Largest Palindromic Number + * [Test Largest Palindromic Number](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/greedy/largest_palindromic_number/test_largest_palindromic_number.py) * Majority Element * [Test Majority Element](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/greedy/majority_element/test_majority_element.py) * Min Arrows diff --git a/algorithms/dynamic_programming/buy_sell_stock/__init__.py b/algorithms/dynamic_programming/buy_sell_stock/__init__.py index 2ea7a4fa..ebf00181 100644 --- a/algorithms/dynamic_programming/buy_sell_stock/__init__.py +++ b/algorithms/dynamic_programming/buy_sell_stock/__init__.py @@ -26,13 +26,6 @@ def max_profit(prices: List[int]) -> int: return current_max_profit -def max_profit_two_pointers(prices: List[int]) -> int: - """ - Variation of max_profit using 2 pointers - Complexity Analysis: - Space: O(1), no extra memory is used - Time: O(n), where n is the size of the input list & we iterate through the list only once - """ def max_profit_two_pointers(prices: List[int]) -> int: """ Variation of max_profit using 2 pointers @@ -46,7 +39,6 @@ def max_profit_two_pointers(prices: List[int]) -> int: number_of_prices = len(prices) left, right = 0, 1 - return 0 left, right = 0, 1 current_max_profit = 0 diff --git a/algorithms/greedy/largest_palindromic_number/README.md b/algorithms/greedy/largest_palindromic_number/README.md new file mode 100644 index 00000000..5a118b68 --- /dev/null +++ b/algorithms/greedy/largest_palindromic_number/README.md @@ -0,0 +1,68 @@ +# Largest Palindromic Number + +You are given a string num consisting of digits from 0 to 9. Your task is to return the largest possible palindromic +number as a string by using some or all of the digits in num. + +The resulting palindromic number must not have leading zeros. + +> Note: You may reorder the digits freely, and you must use at least one digit from the num string. + +## Constraints + +- 1 <= `num.length` <= 1000 +- `num` consists of digits + +## Examples + +![Example 1](./images/examples/largest_palindromic_number_example_1.png) +![Example 2](./images/examples/largest_palindromic_number_example_2.png) +![Example 3](./images/examples/largest_palindromic_number_example_3.png) + +## Solution + +We will use the greedy pattern to solve this problem. The goal is to maximize the size of the palindrome by making +locally optimal choices at each step. Specifically, we aim to form the largest possible palindrome by first prioritizing +using the highest digits. + +The process begins by counting the frequency of each digit in the input string and storing it in a hash table. This +allows us to determine how many times each digit appears. Starting with the highest digit, i.e., 9, and working down to +the lowest, i.e., 0, we try to use the possible pairs of each digit to form the first half of the palindrome. This +ensures that the most significant positions in the palindrome are filled with the largest digits. Out of the leftover +single digits, the highest possible digit can be used as the middle digit to further enhance the size of the palindrome. + +Finally, the palindrome is completed by appending the reverse of the first half to itself, with the middle digit in +between, if applicable. This greedy strategy works effectively because it ensures that each decision made is the best +possible choice at that moment, leading to an overall optimal solution. + +Let’s review the algorithm to reach the solution: + +- Initialize the frequency counter occurrences to count the frequency of each digit in the input string num. +- We also initialize the first_half array to note down the first part of the palindrome and the middle string to track + the middle element of the palindrome. +- Traverse digits from 9 to 0 and for each digit do the following: + - Check if its pairs can be made by looking at its frequency. If yes, then add it to the first_half array. + - If applicable, check for the leading zeros and avoid them by explicitly setting their occurrence to 1. + - Otherwise, check if it can be the middle element of the palindrome. Following the greedy approach, ensures that a + larger number is selected to be the middle element among the elements occurring once. + +- Once we have processed all the elements of the num array, we join the first_half, middle, and reverse of the first_half. +- Finally, we return this palindromic number, the largest that can be generated using the given number. + +![Solution 1](./images/solutions/largest_palindromic_number_solution_1.png) +![Solution 2](./images/solutions/largest_palindromic_number_solution_2.png) +![Solution 3](./images/solutions/largest_palindromic_number_solution_3.png) +![Solution 4](./images/solutions/largest_palindromic_number_solution_4.png) +![Solution 5](./images/solutions/largest_palindromic_number_solution_5.png) +![Solution 6](./images/solutions/largest_palindromic_number_solution_6.png) +![Solution 7](./images/solutions/largest_palindromic_number_solution_7.png) +![Solution 8](./images/solutions/largest_palindromic_number_solution_8.png) +![Solution 9](./images/solutions/largest_palindromic_number_solution_9.png) + +### Time Complexity + +The time complexity of the solution is O(n), where n is the length of the num string. + +### Space Complexity + +The space complexity of the solution is O(n). In the worst case, first_half could store up to n/2 digits if all digits +are the same. Therefore, the space for first_half is O(n). diff --git a/algorithms/greedy/largest_palindromic_number/__init__.py b/algorithms/greedy/largest_palindromic_number/__init__.py new file mode 100644 index 00000000..33f680ef --- /dev/null +++ b/algorithms/greedy/largest_palindromic_number/__init__.py @@ -0,0 +1,65 @@ +from collections import Counter + + +def largest_palindromic_number(num: str) -> str: + # Count the frequency of each digit in the input string + occureneces = Counter(num) + + # first half and the middle of the palindrome + first_half = [] + middle = "" + + for digit in range(9, -1, -1): + digit_char = str(digit) + + if digit_char in occureneces: + digit_count = occureneces[digit_char] + + # num of pairs of this digit that can be used + num_pairs = digit_count // 2 + + # If pairs available, add them to the first half + if num_pairs: + # Avoiding leading zeros + if not first_half and not digit: + occureneces["0"] = 1 + else: + first_half.append(digit_char * num_pairs) + + # Checking for a middle element + if digit_count % 2 and not middle: + middle = digit_char + + # If all elements are '0' + if not middle and not first_half: + return "0" + + # Returning the full palindrome + return "".join(first_half + [middle] + first_half[::-1]) + + +def largest_palindromic_number_v2(num: str) -> str: + # Count how many times each digit appears + digit_counter = Counter(num) + + result = "" + # Find the middle digit by counting down from 9 to 0 while decrementing the count of a digit that appears an odd + # number of times + for digit in range(9, 0, -1): + digit_str = str(digit) + if digit_counter[digit_str] % 2 == 1: + result = digit_str + # decrement the count of this digit + digit_counter[digit_str] -= 1 + break + + # Build symmetric pairs by iterating from 0 to 9 for each digit with remaining count + for number in range(10): + number_str = str(number) + if digit_counter[number_str] > 0: + # Divide by two to get number of pairs + digit_counter[number_str] //= 2 + temp = digit_counter[number_str] * number_str + result = f"{temp}{result}{temp}" + + return result.strip("0") or "0" diff --git a/algorithms/greedy/largest_palindromic_number/images/examples/largest_palindromic_number_example_1.png b/algorithms/greedy/largest_palindromic_number/images/examples/largest_palindromic_number_example_1.png new file mode 100644 index 00000000..14a5117d Binary files /dev/null and b/algorithms/greedy/largest_palindromic_number/images/examples/largest_palindromic_number_example_1.png differ diff --git a/algorithms/greedy/largest_palindromic_number/images/examples/largest_palindromic_number_example_2.png b/algorithms/greedy/largest_palindromic_number/images/examples/largest_palindromic_number_example_2.png new file mode 100644 index 00000000..45c4f52d Binary files /dev/null and b/algorithms/greedy/largest_palindromic_number/images/examples/largest_palindromic_number_example_2.png differ diff --git a/algorithms/greedy/largest_palindromic_number/images/examples/largest_palindromic_number_example_3.png b/algorithms/greedy/largest_palindromic_number/images/examples/largest_palindromic_number_example_3.png new file mode 100644 index 00000000..dff40208 Binary files /dev/null and b/algorithms/greedy/largest_palindromic_number/images/examples/largest_palindromic_number_example_3.png differ diff --git a/algorithms/greedy/largest_palindromic_number/images/solutions/largest_palindromic_number_solution_1.png b/algorithms/greedy/largest_palindromic_number/images/solutions/largest_palindromic_number_solution_1.png new file mode 100644 index 00000000..295f933f Binary files /dev/null and b/algorithms/greedy/largest_palindromic_number/images/solutions/largest_palindromic_number_solution_1.png differ diff --git a/algorithms/greedy/largest_palindromic_number/images/solutions/largest_palindromic_number_solution_2.png b/algorithms/greedy/largest_palindromic_number/images/solutions/largest_palindromic_number_solution_2.png new file mode 100644 index 00000000..658b919c Binary files /dev/null and b/algorithms/greedy/largest_palindromic_number/images/solutions/largest_palindromic_number_solution_2.png differ diff --git a/algorithms/greedy/largest_palindromic_number/images/solutions/largest_palindromic_number_solution_3.png b/algorithms/greedy/largest_palindromic_number/images/solutions/largest_palindromic_number_solution_3.png new file mode 100644 index 00000000..bbcec13e Binary files /dev/null and b/algorithms/greedy/largest_palindromic_number/images/solutions/largest_palindromic_number_solution_3.png differ diff --git a/algorithms/greedy/largest_palindromic_number/images/solutions/largest_palindromic_number_solution_4.png b/algorithms/greedy/largest_palindromic_number/images/solutions/largest_palindromic_number_solution_4.png new file mode 100644 index 00000000..563f676c Binary files /dev/null and b/algorithms/greedy/largest_palindromic_number/images/solutions/largest_palindromic_number_solution_4.png differ diff --git a/algorithms/greedy/largest_palindromic_number/images/solutions/largest_palindromic_number_solution_5.png b/algorithms/greedy/largest_palindromic_number/images/solutions/largest_palindromic_number_solution_5.png new file mode 100644 index 00000000..0eff038c Binary files /dev/null and b/algorithms/greedy/largest_palindromic_number/images/solutions/largest_palindromic_number_solution_5.png differ diff --git a/algorithms/greedy/largest_palindromic_number/images/solutions/largest_palindromic_number_solution_6.png b/algorithms/greedy/largest_palindromic_number/images/solutions/largest_palindromic_number_solution_6.png new file mode 100644 index 00000000..27e9ae04 Binary files /dev/null and b/algorithms/greedy/largest_palindromic_number/images/solutions/largest_palindromic_number_solution_6.png differ diff --git a/algorithms/greedy/largest_palindromic_number/images/solutions/largest_palindromic_number_solution_7.png b/algorithms/greedy/largest_palindromic_number/images/solutions/largest_palindromic_number_solution_7.png new file mode 100644 index 00000000..f29c0bb7 Binary files /dev/null and b/algorithms/greedy/largest_palindromic_number/images/solutions/largest_palindromic_number_solution_7.png differ diff --git a/algorithms/greedy/largest_palindromic_number/images/solutions/largest_palindromic_number_solution_8.png b/algorithms/greedy/largest_palindromic_number/images/solutions/largest_palindromic_number_solution_8.png new file mode 100644 index 00000000..61cd16ab Binary files /dev/null and b/algorithms/greedy/largest_palindromic_number/images/solutions/largest_palindromic_number_solution_8.png differ diff --git a/algorithms/greedy/largest_palindromic_number/images/solutions/largest_palindromic_number_solution_9.png b/algorithms/greedy/largest_palindromic_number/images/solutions/largest_palindromic_number_solution_9.png new file mode 100644 index 00000000..0368c73b Binary files /dev/null and b/algorithms/greedy/largest_palindromic_number/images/solutions/largest_palindromic_number_solution_9.png differ diff --git a/algorithms/greedy/largest_palindromic_number/test_largest_palindromic_number.py b/algorithms/greedy/largest_palindromic_number/test_largest_palindromic_number.py new file mode 100644 index 00000000..233527d0 --- /dev/null +++ b/algorithms/greedy/largest_palindromic_number/test_largest_palindromic_number.py @@ -0,0 +1,32 @@ +import unittest +from parameterized import parameterized +from algorithms.greedy.largest_palindromic_number import ( + largest_palindromic_number, + largest_palindromic_number_v2, +) + +LARGEST_PALINDROMIC_NUMBER_TEST_CASES = [ + ("2012", "212"), + ("000001", "1"), + ("111222333", "3213123"), + ("000000", "0"), + ("123456789123456789", "987654321123456789"), + ("444947137", "7449447"), + ("00009", "9"), +] + + +class LargestPalindromicNumberTestCase(unittest.TestCase): + @parameterized.expand(LARGEST_PALINDROMIC_NUMBER_TEST_CASES) + def test_largest_palindromic_number(self, num: str, expected: str): + actual = largest_palindromic_number(num) + self.assertEqual(expected, actual) + + @parameterized.expand(LARGEST_PALINDROMIC_NUMBER_TEST_CASES) + def test_largest_palindromic_number_v2(self, num: str, expected: str): + actual = largest_palindromic_number_v2(num) + self.assertEqual(expected, actual) + + +if __name__ == "__main__": + unittest.main()