372. Super Pow
Problem Description
The problem requires us to calculate a^b mod 1337
, where a
is a positive integer and b
is an extremely large positive integer presented as an array. The notation a^b
refers to a
raised to the power b
. The array represents the number b
where each element is a digit of b
in its base-10 (decimal) representation. The least significant digit is at the end of the array.
Intuition
To deal with the extremely large exponent b
efficiently, we need to apply modular exponentiation. Calculating a^b
directly, even before applying the modulus, is not feasible because b
can be very large. Instead, we can repeatedly use the property (x * y) mod z = [(x mod z) * (y mod z)] mod z
to keep the intermediary results small.
Also, we can apply the right-to-left binary method for exponentiation, which goes through each digit of the exponent, in our case each element in array b
. For each digit d
in b
from right to left, we compute (a^d) mod 1337
and multiply it with the running ans
. After processing a digit d
, we must update a
to a^10 mod 1337
to account for the positional value of d
in b
. This is because each step to the left in the array represents a decimal place increase by a factor of 10, effectively multiplying our base a
by 10^d
.
By starting from the least significant digit of b
(the end of the array) and working our way back to the most significant digit, each iteration computes the result for one decimal place, while updating a
to its new base raised to the power of 10.
The reason we perform operations under modulo 1337 at every step is to prevent overflow and ensure that all intermediate multiplication results are manageable for large inputs.
Learn more about Math and Divide and Conquer patterns.
Solution Approach
The solution uses a basic loop and modular arithmetic to solve the problem of computing (a^b) mod 1337
. Let's go through the steps:
- Initialize
ans
to 1 because anything raised to the power 0 is 1, and it will be our cumulative product for the result. - Iterate over the digits of
b
in reverse order, i.e., from the least significant digit to the most significant digit. - In each iteration of the loop, we update
ans
using the formulaans = ans * (a^e) mod 1337
. Heree
is the current digit ofb
. The expression(a^e) mod 1337
computes the contribution of the current digit to the overall exponentiation. - After processing each digit
e
, we need to updatea
to account for shifts in decimal place. We use the formulaa = (a^10) mod 1337
. - After the loop ends,
ans
holds the final value of(a^b) mod 1337
.
In this approach, pow(x, y, z)
is a powerful built-in Python function that calculates (x^y) mod z
efficiently (modular exponentiation), even for large values of y
.
By iteratively handling each digit of b
and applying modular arithmetic at every step, we maintain manageable numbers throughout the computation process. This is necessary because working with significantly large numbers directly would be impractical due to computational and memory constraints.
The pattern used here is that of repeated squaring, which is a standard technique in modular exponentiation algorithms where the base is squared (or here raised to the power of 10) for each successive digit. This is premised on the mathematical insight that squaring the base adjusts for the exponential increase as you move through each digit of the exponent.
This also preserves the order of computation represented by the array b
, properly accounting for each digit's positional value, ending with a correct answer modulo 1337.
Ready to land your dream job?
Unlock your dream job with a 2-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's illustrate the solution approach with a small example.
Assume a = 2
, and b
is represented by the array [1, 0, 2]
, which corresponds to the integer 201
.
The goal is to compute (2^201) mod 1337
.
Following the solution steps:
-
Initialize
ans
to 1. Initially,ans = 1
. -
The array
[1, 0, 2]
represents the number201
. We iterate over it from the end (right-to-left):- Starting with the least significant digit, which is
2
in this case, we compute(2^2) mod 1337
. This is4
. - Update
ans
with this value by multiplying:ans = 1 * 4 mod 1337
, which simplifies toans = 4
.
- Starting with the least significant digit, which is
-
Now, before moving to the next digit, we must update
a
toa^10 mod 1337
to account for the positional value:- Update
a
with(2^10) mod 1337
, which is equal to1024
mod1337
. This is1024
. - The new value of
a
is now1024
.
- Update
-
Moving to the second digit from the end, which is
0
:- Compute
(1024^0) mod 1337
, which is1
. - Update
ans = 4 * 1 mod 1337
, soans
remains4
.
- Compute
-
Again, we must update
a
to the next power of ten:- Update
a
with(1024^10) mod 1337
. Here we use the power functionpow(1024, 10, 1337)
. Let's assume this calculation gives us the valueX
for simplicity. - Now
a
becomesX
.
- Update
-
Finally, moving to the most significant digit, which is
1
:- Compute
(X^1) mod 1337
, which just yieldsX
. - Update
ans = 4 * X mod 1337
.
- Compute
-
After processing all digits, the value stored in
ans
is(2^201) mod 1337
.
This example walked through each iteration, updating ans
with the contribution of each digit in the array b
, and recalculating the base a
as a^10 mod 1337
after each step.
Keep in mind that for the actual computing of the updated value of a
as a^10 mod 1337
, especially for large values of a
, the pow
function is used due to its efficient computation of large exponents under a modulus.
Solution Implementation
1class Solution:
2 def superPow(self, base: int, exponent_digits: List[int]) -> int:
3 # Define the modulus to ensure the result is within a reasonable range
4 mod = 1337
5
6 # Initialize the answer to 1, as we'll be multiplying it iteratively
7 result = 1
8
9 # Reverse the list of digits to process the exponent starting from the least significant digit
10 for digit in reversed(exponent_digits):
11 # Multiply the current result by base to the power of the current digit,
12 # then take the modulus to keep it within the defined range.
13 result = (result * pow(base, digit, mod)) % mod
14
15 # Update the base to be raised to the next power of 10, again within the modulus range.
16 # This accounts for shifting one decimal place to the left in the exponent.
17 base = pow(base, 10, mod)
18
19 # Return the final result after processing all digits in the exponent
20 return result
21
1class Solution {
2 private final int MOD = 1337; // Using all-uppercase for the constant value to follow Java naming conventions
3
4 /**
5 * Calculates a^b mod 1337 where a is an integer and b is represented as an array of digits.
6 *
7 * @param a The base number.
8 * @param b The exponent represented as an array of digits.
9 * @return The result of a^b mod 1337.
10 */
11 public int superPow(int a, int[] b) {
12 long result = 1; // Initialize result to 1.
13
14 // Loop through the array from the last element to the first element.
15 for (int i = b.length - 1; i >= 0; --i) {
16 // Multiply the result with the fast power of a raised to the current digit, then take mod.
17 result = result * fastPower(a, b[i]) % MOD;
18 // Update a to a^10 to get the next power level for the next digit.
19 a = fastPower(a, 10);
20 }
21 return (int) result; // Cast result back to int before returning.
22 }
23
24 /**
25 * Fast power algorithm calculates a^n mod 1337.
26 *
27 * @param a The base number.
28 * @param n The exponent.
29 * @return The result of a^n mod 1337.
30 */
31 private long fastPower(long a, int n) {
32 long ans = 1; // Result of the exponentiation.
33
34 // Loop through the bits of n until n is 0.
35 for (; n > 0; n >>= 1) {
36 // Whenever the current bit is 1, multiply ans with a and take mod.
37 if ((n & 1) == 1) {
38 ans = ans * a % MOD;
39 }
40 // Square a and take mod for the next iteration (or next bit).
41 a = a * a % MOD;
42 }
43 return ans; // Return the power result.
44 }
45}
46
1class Solution {
2public:
3 int superPow(int base, vector<int>& digits) {
4 // Define the modulus constant as required by the problem statement.
5 const int MOD = 1337;
6
7 // Result of the super power operation, initialized to 1.
8 long long result = 1;
9
10 // Define a lambda function for fast exponentiation under modulus.
11 // It calculates (base^exponent) % MOD using the binary exponentiation method.
12 auto quickPow = [&](long long base, int exponent) -> int {
13 long long res = 1; // Initialize the result of exponentiation to 1.
14 while (exponent > 0) {
15 if (exponent & 1) {
16 // If the current exponent bit is set, multiply the result by base.
17 res = (res * base) % MOD;
18 }
19 // Square the base and reduce it modulo MOD for the next bit of exponent.
20 base = (base * base) % MOD;
21 // Right-shift exponent by one bit to process the next bit.
22 exponent >>= 1;
23 }
24 // Cast the long long result to int before returning.
25 return static_cast<int>(res);
26 };
27
28 // Process the array of digits in reverse order to calculate the super power.
29 for (int i = digits.size() - 1; i >= 0; --i) {
30 // Multiply the result by (base^(current digit)) % MOD.
31 result = (result * quickPow(base, digits[i])) % MOD;
32 // Update base to base^10 for the next iteration (digit place).
33 base = quickPow(base, 10);
34 }
35
36 // Return the final result as an integer.
37 return static_cast<int>(result);
38 }
39};
40
1function superPow(base: number, digits: number[]): number {
2 let result = 1; // Initialize result variable
3 const modulo = 1337; // Define the modulo value for calculations to avoid large numbers
4
5 // Helper function for quick exponentiation under modulus
6 const quickPow = (base: number, exponent: number): number => {
7 let localResult = 1; // Initialize local result for this function
8 while (exponent > 0) { // Loop until exponent is zero
9 if (exponent & 1) { // If the current bit is set
10 localResult = Number((BigInt(localResult) * BigInt(base)) % BigInt(modulo));
11 }
12 base = Number((BigInt(base) * BigInt(base)) % BigInt(modulo)); // Square the base
13 exponent >>= 1; // Right shift exponent by 1 (divide by 2 and take floor)
14 }
15 return localResult; // Return the calculated power
16 };
17
18 // Start from the least significant digit of the array
19 for (let i = digits.length - 1; i >= 0; --i) {
20 result = Number((BigInt(result) * BigInt(quickPow(base, digits[i]))) % BigInt(modulo));
21 base = quickPow(base, 10); // Elevate the base to the 10th power for the next iteration
22 }
23
24 return result; // Return final result
25}
26
Time and Space Complexity
Time Complexity
The time complexity of the algorithm is determined by the for-loop which iterates through the list b
. Since the length of b
dictates the number of iterations, let's call the length of b
be n
.
During each iteration, there are two main operations.
-
ans = ans * pow(a, e, mod) % mod
: Computational complexity forpow(a, e, mod)
function is generallyO(log(e))
for each iteration. However, sincee
is a single digit (0-9) as per the problem statement ofsuperPow
on LeetCode, the time complexity forpow(a, e, mod)
becomesO(1)
for each iteration. -
a = pow(a, 10, mod)
: Here the basea
is raised to the power of 10. Similar to the previous point, this would normally beO(log(10))
, but since 10 is a constant, this operation is alsoO(1)
.
The loop runs n
times, and each iteration performs a constant number of O(1)
operations. Therefore, the overall time complexity is O(n)
.
Space Complexity
The space complexity of the solution is determined by the extra space used in addition to the input.
- The variable
ans
and other temporary variables use a constant amount of space, contributing to anO(1)
space complexity. - The input list
b
is not duplicated, and reversing the list usingb[::-1]
does not create a new list, it creates an iterator, which is more space-efficient. - There are no recursive calls or additional data structures that would use extra space.
Therefore, the space complexity of the algorithm is O(1)
, as it uses a constant amount of extra space.
Learn more about how to find time and space complexity quickly using problem constraints.
What does the following code do?
1def f(arr1, arr2):
2 i, j = 0, 0
3 new_arr = []
4 while i < len(arr1) and j < len(arr2):
5 if arr1[i] < arr2[j]:
6 new_arr.append(arr1[i])
7 i += 1
8 else:
9 new_arr.append(arr2[j])
10 j += 1
11 new_arr.extend(arr1[i:])
12 new_arr.extend(arr2[j:])
13 return new_arr
14
1public static List<Integer> f(int[] arr1, int[] arr2) {
2 int i = 0, j = 0;
3 List<Integer> newArr = new ArrayList<>();
4
5 while (i < arr1.length && j < arr2.length) {
6 if (arr1[i] < arr2[j]) {
7 newArr.add(arr1[i]);
8 i++;
9 } else {
10 newArr.add(arr2[j]);
11 j++;
12 }
13 }
14
15 while (i < arr1.length) {
16 newArr.add(arr1[i]);
17 i++;
18 }
19
20 while (j < arr2.length) {
21 newArr.add(arr2[j]);
22 j++;
23 }
24
25 return newArr;
26}
27
1function f(arr1, arr2) {
2 let i = 0, j = 0;
3 let newArr = [];
4
5 while (i < arr1.length && j < arr2.length) {
6 if (arr1[i] < arr2[j]) {
7 newArr.push(arr1[i]);
8 i++;
9 } else {
10 newArr.push(arr2[j]);
11 j++;
12 }
13 }
14
15 while (i < arr1.length) {
16 newArr.push(arr1[i]);
17 i++;
18 }
19
20 while (j < arr2.length) {
21 newArr.push(arr2[j]);
22 j++;
23 }
24
25 return newArr;
26}
27
Recommended Readings
Math for Technical Interviews How much math do I need to know for technical interviews The short answer is about high school level math Computer science is often associated with math and some universities even place their computer science department under the math faculty However the reality is that you
https algomonster s3 us east 2 amazonaws com cover_photos dnd svg Divide and Conquer The main idea of divide and conquer is to split the main problem into two smaller components assuming that each one of the components had already been solved recursively and then try to solve the bigger
LeetCode Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way we
Want a Structured Path to Master System Design Too? Don’t Miss This!