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
2 changes: 2 additions & 0 deletions DIRECTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 0 additions & 8 deletions algorithms/dynamic_programming/buy_sell_stock/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
68 changes: 68 additions & 0 deletions algorithms/greedy/largest_palindromic_number/README.md
Original file line number Diff line number Diff line change
@@ -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).
65 changes: 65 additions & 0 deletions algorithms/greedy/largest_palindromic_number/__init__.py
Original file line number Diff line number Diff line change
@@ -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"
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
@@ -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()
Loading