{x}
blog image

Maximum Number of Ways to Partition an Array

You are given a 0-indexed integer array nums of length n. The number of ways to partition nums is the number of pivot indices that satisfy both conditions:

  • 1 <= pivot < n
  • nums[0] + nums[1] + ... + nums[pivot - 1] == nums[pivot] + nums[pivot + 1] + ... + nums[n - 1]

You are also given an integer k. You can choose to change the value of one element of nums to k, or to leave the array unchanged.

Return the maximum possible number of ways to partition nums to satisfy both conditions after changing at most one element.

 

Example 1:

Input: nums = [2,-1,2], k = 3
Output: 1
Explanation: One optimal approach is to change nums[0] to k. The array becomes [3,-1,2].
There is one way to partition the array:
- For pivot = 2, we have the partition [3,-1 | 2]: 3 + -1 == 2.

Example 2:

Input: nums = [0,0,0], k = 1
Output: 2
Explanation: The optimal approach is to leave the array unchanged.
There are two ways to partition the array:
- For pivot = 1, we have the partition [0 | 0,0]: 0 == 0 + 0.
- For pivot = 2, we have the partition [0,0 | 0]: 0 + 0 == 0.

Example 3:

Input: nums = [22,4,-25,-20,-15,15,-16,7,19,-10,0,-13,-14], k = -33
Output: 4
Explanation: One optimal approach is to change nums[2] to k. The array becomes [22,4,-33,-20,-15,15,-16,7,19,-10,0,-13,-14].
There are four ways to partition the array.

 

Constraints:

  • n == nums.length
  • 2 <= n <= 105
  • -105 <= k, nums[i] <= 105

Solution Explanation: Maximum Number of Ways to Partition an Array

This problem asks to find the maximum number of ways to partition an array into two subarrays with equal sums, with the option to change at most one element to a given value k. The solution uses a clever combination of prefix sums and hash tables for efficient counting.

Core Idea:

  1. Prefix Sums: A prefix sum array s is created where s[i] represents the sum of elements from nums[0] to nums[i-1]. This allows for calculating the sum of any subarray in O(1) time.

  2. Hash Tables (Dictionaries): Two hash tables, left and right, are used to store the counts of prefix sums. left[x] stores how many times the prefix sum x appears to the left of a potential pivot, and right[x] stores the same to the right.

  3. Partitioning Condition: A partition is valid if the sum of the left subarray equals the sum of the right subarray. Using prefix sums, this condition translates to s[pivot] == s[n] - s[pivot] (where n is the array length). This simplifies to 2 * s[pivot] == s[n], meaning s[n] must be even for any valid partition.

  4. Handling the Modification: The solution iterates through each element of nums. For each element, it simulates changing it to k. This changes the overall sum by d = k - nums[i]. The new partitioning condition becomes:

    • 2 * s[pivot] == s[n] + d (if the pivot is to the left of the modified element)
    • 2 * (s[pivot] + d) == s[n] + d (if the pivot is to the right of the modified element).
  5. Counting Partitions: After modifying the element, the algorithm checks how many ways the array can be partitioned using the updated prefix sums and hash tables.

Time and Space Complexity:

  • Time Complexity: O(n), where n is the length of nums. This is because the prefix sum calculation, hash table population, and iteration over elements all take linear time.

  • Space Complexity: O(n), primarily due to the prefix sum array and hash tables. In the worst case, all prefix sums could be distinct, resulting in hash tables of size O(n).

Code Explanation (Python):

class Solution:
    def waysToPartition(self, nums: List[int], k: int) -> int:
        n = len(nums)
        s = [0] * (n + 1)  # Prefix sum array (s[0] is 0)
        for i in range(n):
            s[i + 1] = s[i] + nums[i]
 
        right = defaultdict(int)
        for i in range(n):
            right[s[i]] += 1  # Count prefix sums from the right
 
        ans = 0
        if s[n] % 2 == 0:
            ans = right[s[n] // 2]  # Count partitions without modification
 
        left = defaultdict(int)  # Count prefix sums from the left
        for i in range(n):
            d = k - nums[i]
            if (s[n] + d) % 2 == 0:
                target_left = (s[n] + d) // 2
                target_right = (s[n] - d) // 2
                ans = max(ans, left[target_left] + right[target_right]) #Find max partitions after change
            left[s[i+1]] += 1  # Update left count after processing each element
            right[s[i]] -= 1  # Update right count as we process from left to right
        return ans

The Java, C++, and Go code implementations follow the same logic, adapting the syntax and data structures for each language. The core algorithm remains consistent across all four languages.