Count the Number of Beautiful Subarrays
MedPrompt
You are given a 0-indexed integer array nums. In one operation, you can:
- Choose two different indices
iandjsuch that0 <= i, j < nums.length. - Choose a non-negative integer
ksuch that thekthbit (0-indexed) in the binary representation ofnums[i]andnums[j]is1. - Subtract
2kfromnums[i]andnums[j].
A subarray is beautiful if it is possible to make all of its elements equal to 0 after applying the above operation any number of times (including zero).
Return the number of beautiful subarrays in the array nums.
A subarray is a contiguous non-empty sequence of elements within an array.
Note: Subarrays where all elements are initially 0 are considered beautiful, as no operation is needed.
Example 1:
Input: nums = [4,3,1,2,4]
Output: 2
Explanation: There are 2 beautiful subarrays in nums: [4,3,1,2,4] and [4,3,1,2,4].
- We can make all elements in the subarray [3,1,2] equal to 0 in the following way:
- Choose [3, 1, 2] and k = 1. Subtract 21 from both numbers. The subarray becomes [1, 1, 0].
- Choose [1, 1, 0] and k = 0. Subtract 20 from both numbers. The subarray becomes [0, 0, 0].
- We can make all elements in the subarray [4,3,1,2,4] equal to 0 in the following way:
- Choose [4, 3, 1, 2, 4] and k = 2. Subtract 22 from both numbers. The subarray becomes [0, 3, 1, 2, 0].
- Choose [0, 3, 1, 2, 0] and k = 0. Subtract 20 from both numbers. The subarray becomes [0, 2, 0, 2, 0].
- Choose [0, 2, 0, 2, 0] and k = 1. Subtract 21 from both numbers. The subarray becomes [0, 0, 0, 0, 0].Example 2:
Input: nums = [1,10,4]
Output: 0
Explanation: There are no beautiful subarrays in nums.
Constraints:
1 <= nums.length <= 1050 <= nums[i] <= 106
Approaches
2 approaches with complexity analysis and trade-offs.
This approach directly translates the problem into code by checking every possible contiguous subarray. For each subarray, it calculates the XOR sum of its elements. A subarray is beautiful if and only if its XOR sum is zero. While straightforward, this method is inefficient due to its nested loop structure.
Algorithm
- Initialize a counter
beautiful_countto 0. - Iterate through the array with an index
ifrom 0 ton-1. Thisiwill be the starting index of a subarray. - Inside this loop, start another loop with an index
jfromiton-1. Thisjwill be the ending index of the subarray. - For each subarray
nums[i...j], calculate its XOR sum. Maintain acurrent_xorvariable, initialized to 0 for eachi. - In the inner loop, update
current_xorby XORing it withnums[j]. - If
current_xorbecomes 0, it signifies that the subarraynums[i...j]is beautiful. Incrementbeautiful_count. - After both loops complete, return
beautiful_count.
Walkthrough
The fundamental insight is that a subarray can be reduced to all zeros if and only if the XOR sum of all its elements is 0. This is because each operation involves subtracting 2^k from two numbers, which is equivalent to flipping the k-th bit in both. This operation does not change the XOR sum of the subarray. If the final state is all zeros (XOR sum 0), the initial XOR sum must also have been 0.
This brute-force approach iterates through all possible starting points i and ending points j of a subarray. For each subarray nums[i...j], it computes the XOR sum. If the sum is 0, we count it as a beautiful subarray.
class Solution { public long beautifulSubarrays(int[] nums) { long count = 0; int n = nums.length; for (int i = 0; i < n; i++) { int currentXor = 0; for (int j = i; j < n; j++) { currentXor ^= nums[j]; if (currentXor == 0) { count++; } } } return count; }}Complexity
Time
O(N^2), where N is the length of the input array `nums`. We have two nested loops, each potentially running up to N times.
Space
O(1), as we only use a few variables to store the count and the running XOR sum, regardless of the input size.
Trade-offs
Pros
Simple to understand and implement.
Requires minimal extra space (O(1)).
Cons
The O(N^2) time complexity is too slow for the given constraints (N up to 10^5) and will result in a 'Time Limit Exceeded' error on most platforms.
This highly efficient approach solves the problem in linear time by using the concept of prefix XOR sums and a hash map. The problem of finding subarrays with an XOR sum of 0 is cleverly transformed into finding pairs of equal prefix XOR sums. A hash map allows for efficient lookup and storage of these prefix XOR sum frequencies.
Algorithm
- Initialize a hash map
countsto store the frequency of prefix XOR sums. Add an initial entry{0: 1}to handle subarrays that start from index 0. - Initialize
result = 0to count beautiful subarrays andprefix_xor = 0to store the running prefix XOR sum. - Iterate through each
numin the input arraynums. - Update the running prefix XOR:
prefix_xor ^= num. - Check if this
prefix_xorvalue already exists in thecountsmap. The number of times it has appeared before, sayc, corresponds tocsubarrays ending at the current position that have an XOR sum of 0. - Add this count
cto theresult:result += counts.getOrDefault(prefix_xor, 0). - Update the frequency of the current
prefix_xorin the map:counts.put(prefix_xor, counts.getOrDefault(prefix_xor, 0) + 1). - After iterating through all numbers, return
result.
Walkthrough
The core idea remains that a subarray nums[i...j] is beautiful if its XOR sum is 0. We can express the XOR sum of nums[i...j] using prefix XORs. Let P[k] be the XOR sum of nums[0...k]. Then the XOR sum of nums[i...j] is P[j] ^ P[i-1] (with P[-1] defined as 0).
We need P[j] ^ P[i-1] = 0, which implies P[j] == P[i-1]. So, the problem is equivalent to finding the number of pairs of indices (i-1, j) such that their prefix XOR sums are equal.
We can achieve this in a single pass. We iterate through the array, maintaining the current prefix XOR sum. We use a hash map to store the frequencies of all prefix XOR sums encountered so far. For each element, we calculate the new prefix XOR. If we have seen this value k times before, it means there are k starting points that would result in a zero XOR sum subarray ending at the current position. We add k to our total count and then update the map with the new prefix XOR sum.
We initialize the map with a prefix XOR of 0 having a count of 1 to correctly handle subarrays that start from the beginning of the array.
import java.util.HashMap;import java.util.Map; class Solution { public long beautifulSubarrays(int[] nums) { // The map will store the frequency of each prefix XOR sum. // Key: prefix XOR sum, Value: frequency. Map<Integer, Integer> prefixXorCounts = new HashMap<>(); // Initialize with prefix XOR of 0 having a count of 1. // This handles subarrays that start from index 0. prefixXorCounts.put(0, 1); long beautifulSubarrayCount = 0; int currentXor = 0; for (int num : nums) { // Update the current prefix XOR sum. currentXor ^= num; // If we have seen this currentXor value before, it means // there are subarrays ending at the current position // with an XOR sum of 0. int count = prefixXorCounts.getOrDefault(currentXor, 0); beautifulSubarrayCount += count; // Increment the count for the current prefix XOR sum. prefixXorCounts.put(currentXor, count + 1); } return beautifulSubarrayCount; }}Complexity
Time
O(N), where N is the length of `nums`. We perform a single pass through the array, and each hash map operation (get and put) takes O(1) average time.
Space
O(N) in the worst case. If all prefix XOR sums are unique, the hash map will store N+1 entries. The keys are integers whose values are bounded.
Trade-offs
Pros
Extremely efficient with O(N) time complexity, making it suitable for large datasets.
Elegant solution based on a common and powerful pattern for subarray problems involving XOR.
Cons
Requires extra space for the hash map, which can be up to O(N) in the worst-case scenario where all prefix XOR sums are distinct.
Solutions
Solution
class Solution {public long beautifulSubarrays(int[] nums) { Map<Integer, Integer> cnt = new HashMap<>(); cnt.put(0, 1); long ans = 0; int mask = 0; for (int x : nums) { mask ^= x; ans += cnt.getOrDefault(mask, 0); cnt.merge(mask, 1, Integer : : sum); } return ans; }}class Solution {public: long long beautifulSubarrays(vector<int> &nums) { unordered_map<int, int> cnt{{0, 1}}; long long ans = 0; int mask = 0; for (int x : nums) { mask ^= x; ans += cnt[mask]; ++cnt[mask]; } return ans; }};class Solution: def beautifulSubarrays(self, nums: List[int]) -> int: cnt = Counter({0: 1}) ans = mask = 0 for x in nums: mask ^= x ans += cnt[mask] cnt[mask] += 1 return ansVideo walkthrough
Newsletter
One sharp idea, every week
System design and interview prep — short enough to finish.
No spam. Unsubscribe anytime.
Practice
Same difficulty — related problems to reinforce the pattern.