Taro Logo

Sum of Total Strength of Wizards

Hard
Asked by:
Profile picture
Profile picture
9 views
Topics:
ArraysDynamic Programming

As the ruler of a kingdom, you have an army of wizards at your command.

You are given a 0-indexed integer array strength, where strength[i] denotes the strength of the ith wizard. For a contiguous group of wizards (i.e. the wizards' strengths form a subarray of strength), the total strength is defined as the product of the following two values:

  • The strength of the weakest wizard in the group.
  • The total of all the individual strengths of the wizards in the group.

Return the sum of the total strengths of all contiguous groups of wizards. Since the answer may be very large, return it modulo 109 + 7.

A subarray is a contiguous non-empty sequence of elements within an array.

Example 1:

Input: strength = [1,3,1,2]
Output: 44
Explanation: The following are all the contiguous groups of wizards:
- [1] from [1,3,1,2] has a total strength of min([1]) * sum([1]) = 1 * 1 = 1
- [3] from [1,3,1,2] has a total strength of min([3]) * sum([3]) = 3 * 3 = 9
- [1] from [1,3,1,2] has a total strength of min([1]) * sum([1]) = 1 * 1 = 1
- [2] from [1,3,1,2] has a total strength of min([2]) * sum([2]) = 2 * 2 = 4
- [1,3] from [1,3,1,2] has a total strength of min([1,3]) * sum([1,3]) = 1 * 4 = 4
- [3,1] from [1,3,1,2] has a total strength of min([3,1]) * sum([3,1]) = 1 * 4 = 4
- [1,2] from [1,3,1,2] has a total strength of min([1,2]) * sum([1,2]) = 1 * 3 = 3
- [1,3,1] from [1,3,1,2] has a total strength of min([1,3,1]) * sum([1,3,1]) = 1 * 5 = 5
- [3,1,2] from [1,3,1,2] has a total strength of min([3,1,2]) * sum([3,1,2]) = 1 * 6 = 6
- [1,3,1,2] from [1,3,1,2] has a total strength of min([1,3,1,2]) * sum([1,3,1,2]) = 1 * 7 = 7
The sum of all the total strengths is 1 + 9 + 1 + 4 + 4 + 4 + 3 + 5 + 6 + 7 = 44.

Example 2:

Input: strength = [5,4,6]
Output: 213
Explanation: The following are all the contiguous groups of wizards: 
- [5] from [5,4,6] has a total strength of min([5]) * sum([5]) = 5 * 5 = 25
- [4] from [5,4,6] has a total strength of min([4]) * sum([4]) = 4 * 4 = 16
- [6] from [5,4,6] has a total strength of min([6]) * sum([6]) = 6 * 6 = 36
- [5,4] from [5,4,6] has a total strength of min([5,4]) * sum([5,4]) = 4 * 9 = 36
- [4,6] from [5,4,6] has a total strength of min([4,6]) * sum([4,6]) = 4 * 10 = 40
- [5,4,6] from [5,4,6] has a total strength of min([5,4,6]) * sum([5,4,6]) = 4 * 15 = 60
The sum of all the total strengths is 25 + 16 + 36 + 36 + 40 + 60 = 213.

Constraints:

  • 1 <= strength.length <= 105
  • 1 <= strength[i] <= 109

Solution


Clarifying Questions

When you get asked this question in a real-life environment, it will often be ambiguous (especially at FAANG). Make sure to ask these questions in that case:

  1. What are the constraints on the range of values within the `strength` array? Could they be negative, zero, or very large?
  2. How large can the input `strength` array be? I'm thinking about potential overflow issues with large arrays and need to consider optimal algorithmic approaches.
  3. If the `strength` array is empty or null, what should the function return?
  4. Could you clarify what is meant by "total strength" of a contiguous subarray? Specifically, does it involve summing the strengths, finding the minimum strength, or something else?
  5. Are there any constraints on the output value, considering the potential for a very large sum? Should I return the result modulo some number to prevent overflow?

Brute Force Solution

Approach

The brute force approach involves checking every single possible group of wizards and calculating their combined strength. We will consider every possible start and end point for these groups to find the total strength efficiently.

Here's how the algorithm would work step-by-step:

  1. Start by considering the first wizard alone as a group.
  2. Calculate the strength of this group by finding its weakest wizard, and summing the individual strengths of all wizards in the group, then multiplying those two numbers.
  3. Next, consider the first two wizards as a group and repeat the strength calculation process.
  4. Continue expanding the group, adding one wizard at a time, until the group contains all wizards.
  5. Now, shift the starting point. Begin with the second wizard alone and repeat the process of expanding the group until all remaining wizards are included.
  6. Keep shifting the starting wizard and expanding the group until you've considered every possible group of wizards.
  7. Sum the strength of all the groups calculated to get the total strength.

Code Implementation

def sum_of_total_strength_brute_force(strengths):
    total_strength = 0
    number_of_wizards = len(strengths)

    for start_index in range(number_of_wizards):
        for end_index in range(start_index, number_of_wizards):
            # Iterate through all possible sub-arrays (groups of wizards).
            sub_array = strengths[start_index : end_index + 1]
            minimum_strength = min(sub_array)
            sum_of_strengths = sum(sub_array)
            group_strength = minimum_strength * sum_of_strengths
            total_strength += group_strength

    # Mod operation to keep values within bounds to avoid overflow.
    return total_strength % (10**9 + 7)

Big(O) Analysis

Time Complexity
O(n³)The described brute force approach iterates through all possible sub-arrays of the input array of size n. The outer loop iterates n times, selecting the starting wizard of a sub-array. The inner loop expands the group of wizards from the starting wizard to the end of the array, also taking O(n) time. Additionally, within the inner loop, calculating the strength of each sub-array involves finding the minimum element in O(n) time and summing all elements in the sub-array, also taking O(n) time. Therefore, the overall time complexity is O(n * n * n), which simplifies to O(n³).
Space Complexity
O(1)The brute force approach calculates the strength of each sub-array on the fly. It does not explicitly store all possible sub-arrays or intermediate results in auxiliary data structures. The calculations are performed using the input array directly and require only a few constant space variables to keep track of start and end indices, minimum value, and current sum. Therefore, the algorithm's auxiliary space complexity is constant, independent of the input size N.

Optimal Solution

Approach

The key idea is to efficiently compute the strength of all possible subarrays. To avoid redundant calculations, we use prefix sums and a clever trick with a 'next smaller element' approach to identify the minimum strength within each subarray in an efficient way.

Here's how the algorithm would work step-by-step:

  1. Calculate the prefix sums of the wizard strengths. This allows you to find the sum of strengths for any subarray quickly.
  2. For each wizard, find the range of subarrays where they are the weakest wizard. This can be done by finding the next smaller element to the left and right of each wizard.
  3. For each wizard, use the precomputed prefix sums to calculate the total strength of all subarrays where they are the weakest.
  4. Multiply the total strength of each wizard's subarrays by the wizard's strength, and sum up these products. Remember to handle potential overflow issues by using modular arithmetic.

Code Implementation

def sum_of_total_strength(strengths):    modulo = 10**9 + 7
    array_length = len(strengths)

    prefix_sums = [0] * (array_length + 1)
    for i in range(array_length):
        prefix_sums[i + 1] = (prefix_sums[i] + strengths[i]) % modulo

    left_smaller = [0] * array_length
    stack = []
    for i in range(array_length):
        while stack and strengths[stack[-1]] > strengths[i]:
            stack.pop()
        left_smaller[i] = stack[-1] if stack else -1
        stack.append(i)

    right_smaller = [0] * array_length
    stack = []
    for i in range(array_length - 1, -1, -1):
        while stack and strengths[stack[-1]] >= strengths[i]:
            stack.pop()
        right_smaller[i] = stack[-1] if stack else array_length
        stack.append(i)

    total_strength_sum = 0
    for i in range(array_length):
        left_length = i - left_smaller[i]
        right_length = right_smaller[i] - i

        # Calculate the sum of all subarrays where strengths[i] is the minimum.
        sum_left = (i - left_smaller[i]) % modulo
        sum_right = (right_smaller[i] - i) % modulo

        left_prefix_sum = (prefix_sums[i] - prefix_sums[max(i - left_length, 0)]) % modulo
        right_prefix_sum = (prefix_sums[min(i + right_length, array_length)] - prefix_sums[i]) % modulo

        # Apply modular arithmetic to avoid potential overflow
        total_strength = (left_length * right_length) % modulo

        total_strength = (total_strength * strengths[i]) % modulo

        left_sum = (left_length * (left_length + 1) // 2) % modulo
        right_sum = (right_length * (right_length + 1) // 2) % modulo

        total_subarray_strength = (sum_left * sum_right) % modulo

        # Computing the prefix sum is key to efficient subarray sums
        total_subarray_strength = (total_subarray_strength * strengths[i]) % modulo
        total_strength_sum = (total_strength_sum + 
                                total_strength * (
                                  ((right_smaller[i] - i) * prefix_sums[i] % modulo - 
                                   (prefix_sums[max(0, i - left_length)] * (i - left_smaller[i]) % modulo )) % modulo 
                                     - (prefix_sums[i]- prefix_sums[i + 1] if i < array_length - 1 else prefix_sums[i] - prefix_sums[i]) % modulo 
                                   )) % modulo

        total_strength_sum = (total_strength_sum + modulo) % modulo

    return total_strength_sum

Big(O) Analysis

Time Complexity
O(n)Calculating the prefix sums takes O(n) time as we iterate through the input array once. Finding the next smaller element to the left and right for each wizard also takes O(n) time in total using a stack-based approach. Calculating the total strength for each wizard where they are the weakest and summing up these products requires iterating through the array once which is O(n). Therefore, the overall time complexity is dominated by these linear operations, resulting in O(n).
Space Complexity
O(N)The algorithm uses auxiliary space to store prefix sums in an array of size N (where N is the number of wizards), and to find the next smaller element on the left and right, which typically involves using stacks that, in the worst case, can also hold N elements. Thus, we have auxiliary arrays of sizes proportional to N. Therefore the space complexity is O(N).

Edge Cases

Null or empty input array
How to Handle:
Return 0 immediately as there are no wizards to calculate strength for.
Array with a single wizard
How to Handle:
Return the strength of that single wizard raised to the power of 2, modulo the large prime.
Array with all wizards having the same strength
How to Handle:
The prefix sum and sliding window should handle this correctly, but check for potential overflow issues during intermediate calculations.
Array with very large strengths, close to the modulo value
How to Handle:
Ensure intermediate results are taken modulo the large prime to prevent overflow.
Array with strengths including 0
How to Handle:
The minimum value calculation must handle zero values without causing division by zero errors or incorrect strength calculations.
Input array with a large number of wizards (maximum constraints)
How to Handle:
Ensure the prefix sum calculation and sliding window operations scale efficiently to avoid exceeding time limits (O(n) complexity).
Integer overflow during prefix sum calculation
How to Handle:
Use long long data type for prefix sums to prevent integer overflow during intermediate calculations.
Array with negative wizard strengths
How to Handle:
Problem description doesn't explicitly prohibit negative strength, so ensure the minimum value calculation can handle negative numbers correctly when computing the strength of each contiguous sub-array.