Number of Unique XOR Triplets I
MedPrompt
You are given an integer array nums of length n, where nums is a permutation of the numbers in the range [1, n].
A XOR triplet is defined as the XOR of three elements nums[i] XOR nums[j] XOR nums[k] where i <= j <= k.
Return the number of unique XOR triplet values from all possible triplets (i, j, k).
Example 1:
Input: nums = [1,2]
Output: 2
Explanation:
The possible XOR triplet values are:
(0, 0, 0) → 1 XOR 1 XOR 1 = 1(0, 0, 1) → 1 XOR 1 XOR 2 = 2(0, 1, 1) → 1 XOR 2 XOR 2 = 1(1, 1, 1) → 2 XOR 2 XOR 2 = 2
The unique XOR values are {1, 2}, so the output is 2.
Example 2:
Input: nums = [3,1,2]
Output: 4
Explanation:
The possible XOR triplet values include:
(0, 0, 0) → 3 XOR 3 XOR 3 = 3(0, 0, 1) → 3 XOR 3 XOR 1 = 1(0, 0, 2) → 3 XOR 3 XOR 2 = 2(0, 1, 2) → 3 XOR 1 XOR 2 = 0
The unique XOR values are {0, 1, 2, 3}, so the output is 4.
Constraints:
1 <= n == nums.length <= 1051 <= nums[i] <= nnumsis a permutation of integers from1ton.
Approaches
3 approaches with complexity analysis and trade-offs.
The most efficient solution comes from a mathematical observation about the structure of XOR sums over the set of integers {1, ..., n}. By analyzing the problem for small values of n, a pattern emerges. This pattern can be proven to hold for all n, leading to a constant-time solution.
Algorithm
- Handle the base cases for small
n.- If
n = 1, the only possible triplet is(1,1,1)giving1^1^1=1. The result is 1. - If
n = 2, the unique values are{1, 2}. The result is 2.
- If
- For
n >= 3, observe the pattern of the unique values.n=3:{1,2,3}.1^2^3=0. Unique values are{0,1,2,3}. Size 4.n=4:{1,2,3,4}. Triplets generate{0,5,6,7}. Unique values are{0,1,2,3,4,5,6,7}. Size 8.
- Generalize the pattern: For
n >= 3, the set of unique XOR values is{0, 1, ..., p-1}, wherepis the smallest power of 2 that is strictly greater thann. - The size of this set is
p. This can be calculated efficiently using bit manipulation. - Implement the logic: check for
n=1andn=2, otherwise calculate and returnp.
Walkthrough
The key insight is that since nums is a permutation of {1, ..., n}, the problem is independent of the order of elements in nums and only depends on n. The set of unique values is {1, ..., n} igcup {a^b^c | a,b,c ext{ are distinct in } {1,...,n}}.
By analyzing small cases:
- n=1:
nums=[1]. Values:{1}. Size: 1. - n=2:
numsis perm of{1,2}. Values:{1,2}. Size: 2. - n=3:
numsis perm of{1,2,3}. Values:{1,2,3}and1^2^3=0. Total:{0,1,2,3}. Size: 4. - n=4:
numsis perm of{1,2,3,4}. Values:{1,2,3,4}and{1^2^3=0, 1^2^4=7, 1^3^4=6, 2^3^4=5}. Total:{0,1,2,3,4,5,6,7}. Size: 8.
For n >= 3, it can be shown that the set of all possible XOR triplet values forms a complete range of numbers from 0 to p-1, where p is the smallest power of 2 strictly greater than n. The size of this set is p.
This value p can be found efficiently. For a given n, p is 2^k where k is the smallest integer such that 2^k > n. This is equivalent to finding the next power of two after n.
For example, if n=5, the next power of two is 8. If n=8, the next power of two is 16.
This leads to a very simple and fast implementation.
class Solution { public int countUniqueXorTriplets(int[] nums) { int n = nums.length; if (n == 1) { return 1; } if (n == 2) { return 2; } // For n >= 3, the result is the smallest power of 2 strictly greater than n. // This can be found using bit manipulation. // Integer.highestOneBit(n) finds the largest power of 2 less than or equal to n. // If n is a power of 2, say n = 8, highestOneBit(8) is 8. We need 16. So 8 << 1. // If n is not a power of 2, say n = 7, highestOneBit(7) is 4. We need 8. So 4 << 1. // This logic holds for n >= 1, but we've handled n=1,2 separately for clarity. // Let's re-check the formula for n=3: highestOneBit(3) is 2. 2 << 1 = 4. Correct. return Integer.highestOneBit(n) << 1; }}Complexity
Time
O(1), as the solution involves a few comparisons and bitwise operations that do not depend on `n`.
Space
O(1), as no extra space proportional to the input size is needed.
Trade-offs
Pros
Extremely efficient with O(1) time complexity.
Minimal space usage.
Very simple to implement once the mathematical property is known.
Cons
Relies on a non-trivial mathematical property that may not be immediately obvious.
The correctness depends on number-theoretic results about XOR sums.
The most straightforward approach is to simulate the process described in the problem. We can generate every possible triplet (i, j, k) that satisfies the condition i <= j <= k, calculate the XOR sum nums[i] XOR nums[j] XOR nums[k], and store these results in a hash set to keep track of the unique values. The final answer is the size of this set.
Algorithm
- Initialize an empty
HashSetcalleduniqueValuesto store the unique XOR triplet results. - Get the length of the input array
nums, let it ben. - Use three nested loops to iterate through all possible triplets of indices
(i, j, k)such that0 <= i <= j <= k < n. - In the innermost loop, calculate the XOR sum:
value = nums[i] ^ nums[j] ^ nums[k]. - Add the calculated
valueto theuniqueValuesset. The set automatically handles duplicates. - After the loops complete, the number of unique XOR triplets is the size of the
uniqueValuesset. Return this size.
Walkthrough
This method involves a brute-force enumeration of all valid triplets. We use three nested loops. The outer loop runs from i = 0 to n-1, the middle loop from j = i to n-1, and the inner loop from k = j to n-1. This structure ensures that we only consider triplets (i, j, k) where i <= j <= k.
For each triplet, we compute the XOR sum of the corresponding elements from the nums array. To count the number of unique results, we use a HashSet. Each computed XOR value is added to the set. If the value is already present, the set remains unchanged. Finally, the total number of unique values is simply the size of the hash set.
import java.util.HashSet;import java.util.Set; class Solution { public int countUniqueXorTriplets(int[] nums) { int n = nums.length; Set<Integer> uniqueValues = new HashSet<>(); for (int i = 0; i < n; i++) { for (int j = i; j < n; j++) { for (int k = j; k < n; k++) { uniqueValues.add(nums[i] ^ nums[j] ^ nums[k]); } } } return uniqueValues.size(); }}Complexity
Time
O(n^3), where n is the length of the `nums` array. The three nested loops lead to a cubic time complexity, which is too slow for the given constraints.
Space
O(M), where M is the number of unique XOR triplet values. The maximum possible XOR value is bounded by the next power of 2 greater than `n`, so in the worst case, the space is proportional to `n` (e.g., O(2n)).
Trade-offs
Pros
Simple to understand and implement.
Correct for small input sizes.
Cons
Extremely inefficient with a time complexity of O(n^3).
Will result in a 'Time Limit Exceeded' (TLE) error for the given constraints (n <= 10^5).
A more optimized approach uses polynomial multiplication via the Fast Walsh-Hadamard Transform (FWHT) to solve the XOR sumset problem. First, we observe that because nums is a permutation of {1, ..., n}, the problem is equivalent to finding the size of the set V = {1, ..., n} igcup {a^b^c | a,b,c ext{ are distinct elements from } {1, ..., n}}. This set can be expressed as a sumset P_2 igoplus S, where S = {1, ..., n} and P_2 = {a^b | a,b ext{ are distinct in } S}. FWHT allows us to compute the characteristic polynomial of this sumset efficiently.
Algorithm
- Identify the set of possible values: Realize that the set of unique XOR values is the union of
{1, ..., n}and{a^b^c | a,b,c ext{ are distinct in } {1,...,n}}. This can be shown to be equivalent to the set{p^c | p ext{ in } P_2, c ext{ in } S}, whereS = {1,...,n}andP_2 = {a^b | a,b ext{ distinct in } S}. - Represent sets as polynomials: Create boolean arrays (polynomials)
polySandpolyP2of sizeN(smallest power of 2 > 2n) to represent the characteristic functions of the setsSandP_2. - Compute
polyP2using FWHT: The setP_2is(S igoplus S) \setminus \{0\}. Its polynomial can be computed via XOR convolution ofpolySwith itself:poly(S igoplus S) = IFWHT(FWHT(polyS) igodot FWHT(polyS)). Set the coefficient for 0 to be false to getpolyP2. - Compute the final set polynomial: The final set of values is
P_2 igoplus S. Its polynomial is found by another XOR convolution:poly(Result) = IFWHT(FWHT(polyP2) igodot FWHT(polyS)). - Count unique values: The number of unique values is the number of non-zero coefficients in the final polynomial
poly(Result).
Walkthrough
This approach transforms the combinatorial problem into an algebraic one using polynomial convolution.
-
Problem Reformulation: The set of all possible XOR values is
S igcup P_3, whereS = {1, ..., n}andP_3 = {a^b^c | a,b,c ext{ distinct in } S}. This can be shown to be equivalent toP_2 igoplus S = {p^c | p ext{ in } P_2, c ext{ in } S}, whereP_2 = {a^b | a,b ext{ distinct in } S}. -
XOR Convolution: We can find the elements of a sumset
A igoplus Bby computing the XOR convolution of their characteristic polynomials. The FWHT algorithm computes this convolution inO(N log N)time, whereNis the size of the polynomial domain (a power of two large enough to hold all values). -
Algorithm Steps: a. Choose
Nto be the smallest power of 2 greater than2n. b. Create a polynomialpolySforS = {1, ..., n}. c. Use FWHT to computepolyP2frompolySby first computing the convolutionpolyS * polyS. d. Use FWHT again to compute the convolutionpolyP2 * polySto get the final result polynomial. e. Count the number of non-zero entries in the result polynomial.
import java.util.Arrays; class Solution { // This is a conceptual implementation. For competitive programming, // a more optimized FWHT implementation would be used. public int countUniqueXorTriplets(int[] nums) { int n = nums.length; if (n == 0) return 0; int N = 1; while (N <= 2 * n) { N <<= 1; } long[] polyS = new long[N]; for (int i = 1; i <= n; i++) { polyS[i] = 1; } long[] h_S = Arrays.copyOf(polyS, N); fwht(h_S, false); long[] h_S_conv_S = new long[N]; for (int i = 0; i < N; i++) { h_S_conv_S[i] = h_S[i] * h_S[i]; } fwht(h_S_conv_S, true); long[] polyP2 = new long[N]; for (int i = 1; i < N; i++) { if (h_S_conv_S[i] > 0) { polyP2[i] = 1; } } long[] h_P2 = Arrays.copyOf(polyP2, N); fwht(h_P2, false); long[] h_Q = new long[N]; for (int i = 0; i < N; i++) { h_Q[i] = h_P2[i] * h_S[i]; } fwht(h_Q, true); int count = 0; for (int i = 0; i < N; i++) { if (h_Q[i] > 0) { count++; } } return count; } private void fwht(long[] a, boolean inverse) { int n = a.length; for (int len = 1; len < n; len <<= 1) { for (int i = 0; i < n; i += 2 * len) { for (int j = 0; j < len; j++) { long u = a[i + j]; long v = a[i + len + j]; a[i + j] = u + v; a[i + len + j] = u - v; } } } if (inverse) { for (int i = 0; i < n; i++) { a[i] /= n; } } }}Complexity
Time
O(N log N), where N is the smallest power of 2 greater than `2n`. This is dominated by the FWHT computations. For `n=10^5`, this is efficient enough.
Space
O(N), where N is the smallest power of 2 greater than `2n`. Since `n <= 10^5`, `N` can be up to `262144`, so the space is O(n).
Trade-offs
Pros
Significantly more efficient than the brute-force approach.
Can solve the problem within the time limits for the given constraints.
Cons
Complex to implement, requiring knowledge of advanced algorithms like FWHT.
The constant factors in the complexity are relatively large.
Still not the most optimal solution.
Video 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.