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.
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:
- The
targetmay not appear in the array, in which case the result is0. - The array can be very large (up to 10^5 elements), so brute-force checking all O(n²) subarrays is too slow.
- The subarrays are contiguous, so we cannot simply count occurrences globally - we must consider every window where
targetcould 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:
+1ifnums[i] == target-1ifnums[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
- Initialize a prefix sum
balance = 0representing the cumulative "dominance" oftargetover other numbers. - Maintain a map or BIT to store counts of previous balances encountered.
- 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
- No target present: Arrays where