LeetCode 3739 - Count Subarrays With Majority Element II

Here’s a complete, detailed technical solution guide for LeetCode 3739 - Count Subarrays With Majority Element II following your requested format. The problem asks us to count the number of contiguous subarrays in which a given target element is the majority element.

LeetCode Problem 3739

Difficulty: 🔴 Hard
Topics: Array, Hash Table, Divide and Conquer, Segment Tree, Merge Sort, Prefix Sum

Solution

Here’s a complete, detailed technical solution guide for LeetCode 3739 - Count Subarrays With Majority Element II following your requested format.

Problem Understanding

The problem asks us to count the number of contiguous subarrays in which a given target element is the majority element. A majority element is one that appears strictly more than half of the length of the subarray.

The input consists of an integer array nums of length up to 10^5 and an integer target. The output is a single integer, the count of subarrays where target is the majority.

Important aspects to notice:

  1. The target may not appear in the array, in which case the result is 0.
  2. The array can be very large (up to 10^5 elements), so brute-force checking all O(n²) subarrays is too slow.
  3. The subarrays are contiguous, so we cannot simply count occurrences globally - we must consider every window where target could dominate.

Edge cases include arrays where every element is the target, arrays with no occurrences of the target, and arrays with multiple candidates close in frequency to the target.

Approaches

Brute Force Approach

The naive solution iterates through all possible subarrays. For each subarray, we count the occurrences of target and check if it is greater than half the length of the subarray. While correct, this is infeasible for large inputs:

  • Time Complexity: O(n²) for iterating subarrays × O(n) for counting → O(n³) in total.
  • Space Complexity: O(1), aside from loop variables.

This is clearly too slow given n can be 10^5.

Optimal Approach

The key insight is to transform the problem into a prefix sum / balance problem. For each index in the array, consider a "balance" value:

  • +1 if nums[i] == target
  • -1 if nums[i] != target

Then, the target is the majority in a subarray if the sum of this balance is strictly positive.

We can use a prefix sum array of these balances and count the number of pairs (i, j) such that the difference of prefix sums from i to j is positive. This reduces to counting the number of previous prefix sums that are strictly less than the current prefix sum.

Using a Fenwick Tree / Binary Indexed Tree (BIT) or Balanced BST, we can do this efficiently in O(n log n) time.

Approach Time Complexity Space Complexity Notes
Brute Force O(n³) O(1) Check all subarrays
Prefix Sum + BIT O(n log n) O(n) Transform to balance and count prefix sums

Algorithm Walkthrough

  1. Initialize a prefix sum balance = 0 representing the cumulative "dominance" of target over other numbers.
  2. Maintain a map or BIT to store counts of previous balances encountered.
  3. Iterate through the array:

a. Update balance to balance +1 if current element equals target, else balance -1.

b. Query the BIT or map for the number of previous balances strictly less than the current balance. This count is the number of subarrays ending at the current index where target is the majority.

c. Increment the count of the current balance in the BIT/map. 4. Sum all counts to get the final answer.

Why it works:

By representing target presence as +1 and others as -1, a positive prefix sum in any subarray indicates that target occurs more than half the times in that subarray. Counting the number of previous prefix sums smaller than the current gives all starting indices i that lead to a valid majority subarray ending at j.

Python Solution

from typing import List
from collections import defaultdict
import bisect

class Solution:
    def countMajoritySubarrays(self, nums: List[int], target: int) -> int:
        prefix_balance = 0
        prefix_counts = defaultdict(int)
        prefix_counts[0] = 1  # empty prefix
        balances_sorted = [0]  # maintain sorted unique balances
        result = 0
        
        for num in nums:
            if num == target:
                prefix_balance += 1
            else:
                prefix_balance -= 1

            # count of previous balances strictly less than current
            idx = bisect.bisect_left(balances_sorted, prefix_balance)
            count = sum(prefix_counts[b] for b in balances_sorted[:idx])
            result += count

            # update map and sorted list if needed
            prefix_counts[prefix_balance] += 1
            if prefix_balance not in balances_sorted:
                bisect.insort(balances_sorted, prefix_balance)

        return result

Explanation:

We maintain prefix_balance to represent the net majority status of target. Using a defaultdict, we store counts of previous balances. balances_sorted keeps track of unique prefix sums for efficient querying using binary search (bisect). At each step, we count how many previous prefix sums are strictly less than the current to find valid subarrays ending at that index.

Go Solution

package main

func countMajoritySubarrays(nums []int, target int) int64 {
    balance := 0
    result := int64(0)
    prefixCounts := map[int]int{}
    prefixCounts[0] = 1
    balancesSorted := []int{0}

    for _, num := range nums {
        if num == target {
            balance += 1
        } else {
            balance -= 1
        }

        // Count previous balances strictly less than current
        count := 0
        for _, b := range balancesSorted {
            if b < balance {
                count += prefixCounts[b]
            } else {
                break
            }
        }
        result += int64(count)

        // Update counts and sorted balances
        prefixCounts[balance] += 1
        inserted := false
        for i, b := range balancesSorted {
            if b == balance {
                inserted = true
                break
            } else if b > balance {
                balancesSorted = append(balancesSorted[:i], append([]int{balance}, balancesSorted[i:]...)...)
                inserted = true
                break
            }
        }
        if !inserted {
            balancesSorted = append(balancesSorted, balance)
        }
    }
    return result
}

Go-specific notes:

We handle map initialization and integer arithmetic explicitly. We use slice manipulation to maintain balancesSorted. Unlike Python, Go lacks built-in bisect, so we perform linear search for simplicity, though a Fenwick Tree would optimize to O(n log n).

Worked Examples

Example 1

nums = [1,2,2,3], target = 2

i num balance prefix_counts valid subarrays ending here result
0 1 -1 {0:1, -1:1} 0 0
1 2 0 {-1:1,0:2} 1 1
2 2 1 {-1:1,0:2,1:1} 2 3
3 3 0 {-1:1,0:3,1:1} 2 5

Result = 5

Example 2

nums = [1,1,1,1], target = 1

All balances increase, all subarrays counted, total = 10.

Example 3

nums = [1,2,3], target = 4

target never appears, balances never positive → result = 0.

Complexity Analysis

Measure Complexity Explanation
Time O(n log n) For each of n elements, binary search in sorted prefix balances
Space O(n) Store prefix_counts and sorted unique balances

The algorithm avoids O(n²) brute-force by leveraging prefix sums and efficient counting of previous prefix balances.

Test Cases

# provided examples
assert Solution().countMajoritySubarrays([1,2,2,3], 2) == 5  # example 1
assert Solution().countMajoritySubarrays([1,1,1,1], 1) == 10  # example 2
assert Solution().countMajoritySubarrays([1,2,3], 4) == 0  # example 3

# edge/boundary cases
assert Solution().countMajoritySubarrays([2], 2) == 1  # single element equals target
assert Solution().countMajoritySubarrays([1], 2) == 0  # single element not target
assert Solution().countMajoritySubarrays([1,2,2,2,3,2,2], 2) == 16  # multiple overlapping subarrays
Test Why
single element target minimal input where target exists
single element non-target minimal input with no target
overlapping subarrays checks correct counting across multiple windows

Edge Cases

  1. No target present: Arrays where