From e431e03cf1f72f4e8b983cb98b2975448069a17c Mon Sep 17 00:00:00 2001 From: BrianLusina <12752833+BrianLusina@users.noreply.github.com> Date: Tue, 21 Apr 2026 11:05:35 +0300 Subject: [PATCH 1/3] feat(algorithms, hash-table): powerful integers --- .../max_subarray/README.md | 33 +++++ .../hash_table/powerful_integers/README.md | 127 ++++++++++++++++++ .../hash_table/powerful_integers/__init__.py | 55 ++++++++ .../test_powerful_integers.py | 37 +++++ .../trie/index_pairs_of_a_string/__init__.py | 1 + 5 files changed, 253 insertions(+) create mode 100644 algorithms/hash_table/powerful_integers/README.md create mode 100644 algorithms/hash_table/powerful_integers/__init__.py create mode 100644 algorithms/hash_table/powerful_integers/test_powerful_integers.py diff --git a/algorithms/dynamic_programming/max_subarray/README.md b/algorithms/dynamic_programming/max_subarray/README.md index 380d028d..3962ed6a 100644 --- a/algorithms/dynamic_programming/max_subarray/README.md +++ b/algorithms/dynamic_programming/max_subarray/README.md @@ -20,3 +20,36 @@ find_subarr_maxsum([4, -1, 2, 1, -40, 1, 2, -1, 4]) == [[[4, -1, 2, 1], [1, 2, - ``` If the array does not have any sub-array with a positive sum of its terms, the function will return [[], 0]. + +## Solution + +We’ll solve this problem using Kadane’s Algorithm. It’s a dynamic programming approach. The key idea is that we can +efficiently find the maximum subarray ending at any position based on the maximum subarray ending at the previous +position. + +The subproblem here is finding the maximum subarray sum that ends at a specific index i. We need to calculate this for +every index i in the array. The base case is the first element of the array, where both current subarray sum and maximum +subarray sum are initialized with the first element’s value. This is the starting point for solving the subproblems. At +each step, we reuse the previously computed maximum subarray sum to find the solution for the current subproblem. + +The steps of the algorithm are given below: + +1. Initialize two variables, current_sum and max_sum, with the value of the first element in the input list. These + variables are used to keep track of the current sum of the subarray being considered, and the maximum sum found so + far. +2. Iterate through the input list, starting from the second element to the end of the list. Within the loop, perform the + following steps: + - If current_sum + nums[i] is smaller than nums[i], this indicates that starting a new subarray with nums[i] would + yield a larger sum than extending the previous subarray. In such cases, reset current_sum to nums[i]. + - Compare the current max_sum with the updated current_sum. This step ensures that the max_sum always stores the + maximum sum encountered so far. +3. After the loop completes, the function returns the max_sum, which represents the maximum sum of any contiguous + subarray within the input list. + +### Time Complexity +The time complexity of this solution is O(n), because we are iterating the array once, where n is the total number of +elements in the array. + +### Space Complexity + +The space complexity of this solution is O(1). diff --git a/algorithms/hash_table/powerful_integers/README.md b/algorithms/hash_table/powerful_integers/README.md new file mode 100644 index 00000000..c7db22f0 --- /dev/null +++ b/algorithms/hash_table/powerful_integers/README.md @@ -0,0 +1,127 @@ +# Powerful Integers + +Given three integers x, y, and bound, return a list of all the powerful integers that have a value less than or equal to +bound. + +An integer is powerful if it can be represented as xi + yj for some integers i >= 0 and j >= 0. + +You may return the answer in any order. In your answer, each value should occur at most once. + +## Examples + +Example 1: + +```text +Input: x = 2, y = 3, bound = 10 +Output: [2,3,4,5,7,9,10] +Explanation: +2 = 20 + 30 +3 = 21 + 30 +4 = 20 + 31 +5 = 21 + 31 +7 = 22 + 31 +9 = 23 + 30 +10 = 20 + 32 +``` + +Example 2: +```text +Input: x = 3, y = 5, bound = 15 +Output: [2,4,6,8,10,14] +``` + +## Constraints + +- 1 <= x, y <= 100 +- 0 <= bound <= 10^6 + +## Topics + +- Hash Table +- Math +- Enumeration + +## Solution(s) + +1. [Logarithmic Bounds](#logarithmic-bounds) +2. [Logarithmic Bound 2](#logarithmic-bound-2) + +### Logarithmic Bounds + +Our approach here will only focus on finding the bounds for numbers x and y. One way to get the bounds on the powers is +to have nested loops that iterate from [0⋯bound]. However, this is very inefficient because the bound can be an extremely +large value and a nested-loop over this bound will take forever to finish. Also, we don't need to iterate over all of the +values and combinations. There is a way to find a much smaller bound for the powers. + +m^n <= bound + +This formula implies that + +n<=log(m) bound + +> We can use the log function to determine the bounds for the powers of "x" and "y". + +#### Algorithm + +1. Let's define `a` as the power bound for the number `x`. Thus `a=log(x)bound`. +2. Similarly, let's define `b` as the power bound for the number `y`. Thus `b=log(y)bound`. +3. Now we will have our nested for-loop structure where the outer loop will iterate from [0⋯a] and the inner loop will + iterate from [0⋯b]. +4. We will use a set to store our results. This is because we might generate the same value multiple times. E.g. + `2^1 + 3^2 = 11` and `2^3 + 3^1 = 11`. We only need to include the value 11 once and hence, we will use a set called + `resultSet` to store our answers. +5. At each step, we calculate `x^a + y^b` and check if this value is less than or equal to bound. If it is, then this is + a powerful integer and we add it to our set of answers. +6. We need special break conditions to handle the scenario when x or y is 1. This is because if the number x or y is 1, + then their power-bound will be equal to bound itself. Also, it doesn't matter what their power-bound is because 1^N + is always 1. Thus, when the number is 1, we don't need to loop from [0⋯N] and we can break early. +7. Finally, convert the set to a list and return. + +#### Time Complexity + +Time Complexity: Let `N` be `log(x)bound` and `M` be `log(y)bound`. Then the overall time complexity is `O(N×M)` because +we used a nested loop structure to calculate all of the powerful integers. + +#### Space Complexity + +`O(N×M)` because we use a set to omit duplicates. We could just use our result list to check membership before adding +values. However, that would be costly in terms of time complexity because it would require a full scan of the result list +to see if the value already exists. + +### Logarithmic Bound 2 + +The key insight is that since x^i and y^j grow exponentially, the number of distinct powers of x and y that remain within +bound is at most O(log(bound) each. We can enumerate all pairs (i,j) by iterating through powers of x in an outer loop +and powers of y in an inner loop, adding each sum x^i + y^j that is less than or equal to bound into a hash set called +`resultSet`. The set automatically handles deduplication, ensuring each powerful integer appears at most once. A special +case arises when x or y equals 1, because 1^i is always 1 regardless of the exponent, which would cause an infinite loop. +We handle this by breaking out of the respective loop after the first iteration when x or y is 1. + +Solution steps: + +1. Initialize an empty set resultSet to store unique powerful integers. +2. Initialize `powX` to 1, representing x^0 +3. Start an outer while loop that continues as long as `powX ≤ bound`. + - Inside the outer loop, initialize `powY` to 1, representing y^0 + - Start an inner while loop that continues as long as `powX + powY ≤ bound`. + - Add the value `powX` + `powY` to `resultSet`. + - If y equals 1, break out of the inner loop immediately, since y^j will always be 1 and further iterations would + not produce new values. + - Otherwise, multiply `powY` by y to advance to the next power of y. + - After the inner loop, if x equals 1, break out of the outer loop immediately, since x^i will always be 1 and further + iterations would not produce new values. + - Otherwise, multiply `powX` by x to advance to the next power of x. +4. Convert resultSet to a list and return it. + +#### Time Complexity + +The time complexity of the solution is O(logx(bound)) * logy(bound) because the outer loop runs at most O(logx(bound)) +times and the inner loop runs at most O(logy(bound)) times for each iteration of the outer loop. Since bound ≤10^6 and +the minimum base greater than 1 is 2, each loop runs at most about log2(10^6)≈20 iterations, making this very efficient. +When x or y is 1, the corresponding loop runs only once. + +#### Space Complexity + +The space complexity is `O(logx(bound)) * logy(bound)` because in the worst case, every combination of powers produces +a unique sum, and all of these are stored in the resultSet. This is bounded by the total number of pairs enumerated, +which is at most `O(logx(bound)) * logy(bound)`. diff --git a/algorithms/hash_table/powerful_integers/__init__.py b/algorithms/hash_table/powerful_integers/__init__.py new file mode 100644 index 00000000..0eeed221 --- /dev/null +++ b/algorithms/hash_table/powerful_integers/__init__.py @@ -0,0 +1,55 @@ +from typing import List, Set +from math import log + + +def powerful_integers(x: int, y: int, bound: int) -> List[int]: + # Use a set to store unique powerful integers + result_set: Set[int] = set() + + # Compute powers of x up to bound + # If x == 1, x^i is always 1, so only need i=0 + pow_x = 1 # x^0 = 1 + while pow_x <= bound: + # For each power of x, iterate over powers of y + pow_y = 1 # y^0 = 1 + while pow_x + pow_y <= bound: + # Add the powerful integer to the set + result_set.add(pow_x + pow_y) + # If y is 1, y^j is always 1, so break after first iteration + if y == 1: + break + # Move to next power of y + pow_y *= y + # If x is 1, x^i is always 1, so break after first iteration + if x == 1: + break + # Move to next power of x + pow_x *= x + + # Convert set to list and return + return list(result_set) + + +def powerful_integers_logarithmic_bounds(x: int, y: int, bound: int) -> List[int]: + if bound == 0: + return [] + + a = bound if x == 1 else int(log(bound, x)) + b = bound if y == 1 else int(log(bound, y)) + + result_set = set([]) + + for i in range(a + 1): + for j in range(b + 1): + value = x**i + y**j + + if value <= bound: + result_set.add(value) + + if y == 1: + break + + if x == 1: + break + + return list(result_set) diff --git a/algorithms/hash_table/powerful_integers/test_powerful_integers.py b/algorithms/hash_table/powerful_integers/test_powerful_integers.py new file mode 100644 index 00000000..87c4437e --- /dev/null +++ b/algorithms/hash_table/powerful_integers/test_powerful_integers.py @@ -0,0 +1,37 @@ +import unittest +from typing import List +from parameterized import parameterized +from algorithms.hash_table.powerful_integers import ( + powerful_integers, + powerful_integers_logarithmic_bounds, +) + +POWERFUL_INTEGERS_TEST_CASE = [ + (2, 2, 20, [2, 3, 4, 5, 6, 8, 9, 10, 12, 16, 17, 18, 20]), + (1, 1, 5, [2]), + (5, 3, 50, [2, 4, 6, 8, 10, 14, 26, 28, 32, 34]), + (100, 100, 1000000, [2, 101, 200, 10001, 10100, 20000]), + (2, 5, 0, []), + (2, 3, 10, [2, 3, 4, 5, 7, 9, 10]), + (3, 5, 15, [2, 4, 6, 8, 10, 14]), +] + + +class PowerfulIntegersTestCase(unittest.TestCase): + @parameterized.expand(POWERFUL_INTEGERS_TEST_CASE) + def test_powerful_integers(self, x: int, y: int, bound: int, expected: List[int]): + actual = powerful_integers(x, y, bound) + actual.sort() + self.assertEqual(expected, actual) + + @parameterized.expand(POWERFUL_INTEGERS_TEST_CASE) + def test_powerful_integers_logarithmic_bounds( + self, x: int, y: int, bound: int, expected: List[int] + ): + actual = powerful_integers_logarithmic_bounds(x, y, bound) + actual.sort() + self.assertEqual(expected, actual) + + +if __name__ == "__main__": + unittest.main() diff --git a/algorithms/trie/index_pairs_of_a_string/__init__.py b/algorithms/trie/index_pairs_of_a_string/__init__.py index 25332a23..661ff751 100644 --- a/algorithms/trie/index_pairs_of_a_string/__init__.py +++ b/algorithms/trie/index_pairs_of_a_string/__init__.py @@ -1,6 +1,7 @@ from typing import List from datastructures.trees.trie import Trie + def index_pairs(text: str, words: List[str]) -> List[List[int]]: trie = Trie() From 1ce9d474fd032ece5a13cabd84086e4213704602 Mon Sep 17 00:00:00 2001 From: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Date: Tue, 21 Apr 2026 08:06:00 +0000 Subject: [PATCH 2/3] updating DIRECTORY.md --- DIRECTORY.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/DIRECTORY.md b/DIRECTORY.md index 8b368b28..b6eae6d2 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -217,6 +217,8 @@ * [Test First Unique Character](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/hash_table/first_unique_character/test_first_unique_character.py) * Jewels And Stones * [Test Jewels And Stones](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/hash_table/jewels_and_stones/test_jewels_and_stones.py) + * Powerful Integers + * [Test Powerful Integers](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/hash_table/powerful_integers/test_powerful_integers.py) * Ransom Note * [Test Ransom Note](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/hash_table/ransom_note/test_ransom_note.py) * Heap From 50a762d7ce120ffe98f6137595c7ea54e42c32cb Mon Sep 17 00:00:00 2001 From: Lusina <12752833+BrianLusina@users.noreply.github.com> Date: Tue, 21 Apr 2026 11:27:37 +0300 Subject: [PATCH 3/3] Update algorithms/dynamic_programming/max_subarray/README.md Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- algorithms/dynamic_programming/max_subarray/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/algorithms/dynamic_programming/max_subarray/README.md b/algorithms/dynamic_programming/max_subarray/README.md index 3962ed6a..58074818 100644 --- a/algorithms/dynamic_programming/max_subarray/README.md +++ b/algorithms/dynamic_programming/max_subarray/README.md @@ -47,7 +47,7 @@ The steps of the algorithm are given below: subarray within the input list. ### Time Complexity -The time complexity of this solution is O(n), because we are iterating the array once, where n is the total number of +The time complexity of this solution is O(n) because we are iterating the array once, where n is the total number of elements in the array. ### Space Complexity