Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions problems/3514-number-of-unique-xor-triplets-ii/analysis.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# 3514. Number of Unique XOR Triplets II

[LeetCode Link](https://leetcode.com/problems/number-of-unique-xor-triplets-ii/)

Difficulty: Medium
Topics: Array, Math, Bit Manipulation, Enumeration
Acceptance Rate: 40.2%

## Hints

### Hint 1

The brute-force idea is to iterate over every triplet `(i, j, k)` with `i <= j <= k`, XOR the three values, and drop the results into a set. With `n` up to 1500 that is on the order of `n^3 / 6 ≈ 5 * 10^8` operations — too slow. Before optimizing, ask yourself: what is special about the *values* here, and does the ordering `i <= j <= k` actually restrict which XOR results are reachable?

### Hint 2

Because indices may repeat (`i <= j <= k` allows `i == j == k`), the ordering constraint adds nothing: you can reach every value `a ^ b ^ c` where `a`, `b`, `c` are chosen from the array with repetition. XOR is commutative and associative, so only the *set of distinct values* matters, not positions or counts. Reduce `nums` to its distinct values first.

### Hint 3

Every `nums[i]` is at most 1500, so any XOR of these values stays below 2048 (11 bits). That means the number of *reachable* XOR values is tiny — bounded by 2048 — no matter how large `n` is. Split the work in two stages: first compute the set of all pairwise XORs `P = { x ^ y }`, then XOR that set against the distinct values once more to get all triplet XORs. Each stage is bounded by `|distinct| * 2048`, which is comfortably fast.

## Approach

Let `D` be the set of distinct values in `nums`.

1. **Reduce to distinct values.** Duplicates never create a new XOR result, so collapse `nums` to `D`. Since `1 <= nums[i] <= 1500`, `|D| <= 1500`.

2. **Build the pairwise-XOR set `P`.** For every ordered pair `(x, y)` with `x, y in D`, record `x ^ y`. This automatically includes `0` (from `x ^ x`) and every single value `x` is representable later. Because each value is under 2048, `P` lives in a boolean array of size 2048. This costs `O(|D|^2)` time.

3. **Build the triplet-XOR set `T`.** For every `p in P` and every `d in D`, record `p ^ d`. This yields every `x ^ y ^ z` for `x, y, z in D`, which is exactly the set of reachable triplet values. This costs `O(|P| * |D|) = O(2048 * |D|)`.

4. **Count** the number of `true` entries in `T`.

Why the two-stage split works: a triplet XOR is `(x ^ y) ^ z`. Stage 2 collects all pairwise results into `P`, and stage 3 combines each of them with a third distinct value. Because we already store `0 in P`, values reachable with "fewer distinct picks" are also covered — e.g. `p = 0` gives back the single elements `d`, and `p = x ^ y` with `z` reproducing one of them gives back a single value too.

**Walkthrough on `nums = [1, 3]`:** `D = {1, 3}`. Pairwise XORs: `1^1 = 0`, `1^3 = 2`, `3^3 = 0`, so `P = {0, 2}`. Triplet XORs: `0^1 = 1`, `0^3 = 3`, `2^1 = 3`, `2^3 = 1`, so `T = {1, 3}`. Count is `2`. ✅

## Complexity Analysis

Let `V = 2048` be the value bound (next power of two above the max XOR) and `d = |D| <= 1500`.

Time Complexity: `O(d^2 + V * d)` — the pairwise stage dominates at roughly `d^2` (about `2.25 * 10^6`), and the triplet stage is `V * d`. Both are well within limits.
Space Complexity: `O(V + d)` — two boolean arrays of size `V` for `P` and `T`, plus the distinct-value list.

## Edge Cases

- **Single element (`n == 1`):** the only triplet is `a ^ a ^ a = a`, so the answer is `1`. The distinct set is `{a}`, `P = {0}`, and `T = {a}` — handled naturally.
- **All identical elements:** collapses to a single distinct value, same as the `n == 1` case; answer is `1`.
- **Two elements:** `P = {0, a^b}` and `T = {a, b}`, giving `2` distinct triplet values (matches Example 1). Confirms the ordering constraint imposes no real restriction.
- **Values that XOR to `0`:** `0` is always reachable via `a ^ a ^ a`... but note `0` only appears in `T` if some `d` cancels a `p`; for a single distinct value the smallest result is `a` itself, so make sure the sizing (2048) covers the largest possible XOR, not just the largest input.
64 changes: 64 additions & 0 deletions problems/3514-number-of-unique-xor-triplets-ii/problem.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
---
number: "3514"
frontend_id: "3514"
title: "Number of Unique XOR Triplets II"
slug: "number-of-unique-xor-triplets-ii"
difficulty: "Medium"
topics:
- "Array"
- "Math"
- "Bit Manipulation"
- "Enumeration"
acceptance_rate: 4023.6
is_premium: false
created_at: "2026-07-24T03:51:53.056616+00:00"
fetched_at: "2026-07-24T03:51:53.056616+00:00"
link: "https://leetcode.com/problems/number-of-unique-xor-triplets-ii/"
date: "2026-07-24"
---

# 3514. Number of Unique XOR Triplets II

You are given an integer array `nums`.

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,3]

**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 3 = 3`
* `(0, 1, 1) -> 1 XOR 3 XOR 3 = 1`
* `(1, 1, 1) -> 3 XOR 3 XOR 3 = 3`



The unique XOR values are `{1, 3}`. Thus, the output is 2.

**Example 2:**

**Input:** nums = [6,7,8,9]

**Output:** 4

**Explanation:**

The possible XOR triplet values are `{6, 7, 8, 9}`. Thus, the output is 4.



**Constraints:**

* `1 <= nums.length <= 1500`
* `1 <= nums[i] <= 1500`
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package main

// Number of Unique XOR Triplets II
//
// Approach: Because i <= j <= k permits repeated indices and XOR is
// commutative/associative, the reachable triplet values are exactly
// { x ^ y ^ z : x, y, z in distinct(nums) }. Every value is <= 1500, so any
// XOR stays below 2048 (11 bits). We first compute the set of pairwise XORs P,
// then XOR P against the distinct values to obtain every triplet XOR, using
// fixed-size boolean arrays as bitsets.
func uniqueXorTriplets(nums []int) int {
const limit = 2048 // next power of two above the max XOR of values <= 1500

// Collect distinct values.
seen := make([]bool, limit)
distinct := make([]int, 0, len(nums))
for _, v := range nums {
if !seen[v] {
seen[v] = true
distinct = append(distinct, v)
}
}

// Stage 1: all pairwise XORs (includes 0 from x ^ x).
pair := make([]bool, limit)
for i := 0; i < len(distinct); i++ {
for j := i; j < len(distinct); j++ {
pair[distinct[i]^distinct[j]] = true
}
}

// Stage 2: XOR each pairwise result with each distinct value -> triplet XORs.
triplet := make([]bool, limit)
for p := 0; p < limit; p++ {
if !pair[p] {
continue
}
for _, d := range distinct {
triplet[p^d] = true
}
}

// Count reachable triplet values.
count := 0
for _, ok := range triplet {
if ok {
count++
}
}
return count
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package main

import "testing"

func TestUniqueXorTriplets(t *testing.T) {
tests := []struct {
name string
nums []int
expected int
}{
{"example 1: two elements {1,3} -> {1,3}", []int{1, 3}, 2},
{"example 2: {6,7,8,9} -> {6,7,8,9}", []int{6, 7, 8, 9}, 4},
{"edge case: single element yields only itself", []int{5}, 1},
{"edge case: all identical collapses to one value", []int{7, 7, 7, 7}, 1},
{"edge case: duplicates do not add new values", []int{1, 1, 3, 3}, 2},
{"edge case: value 1 alone", []int{1}, 1},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := uniqueXorTriplets(tt.nums)
if got != tt.expected {
t.Errorf("uniqueXorTriplets(%v) = %d, want %d", tt.nums, got, tt.expected)
}
})
}
}

// TestUniqueXorTripletsBruteForce cross-checks the optimized solution against a
// straightforward O(n^3) reference on small random-ish inputs.
func TestUniqueXorTripletsBruteForce(t *testing.T) {
inputs := [][]int{
{1, 3},
{6, 7, 8, 9},
{5},
{2, 4, 6, 8, 10},
{1, 2, 3, 4, 5, 6},
{1500, 1, 1499, 2},
}

brute := func(nums []int) int {
set := map[int]struct{}{}
n := len(nums)
for i := 0; i < n; i++ {
for j := i; j < n; j++ {
for k := j; k < n; k++ {
set[nums[i]^nums[j]^nums[k]] = struct{}{}
}
}
}
return len(set)
}

for _, in := range inputs {
want := brute(in)
got := uniqueXorTriplets(in)
if got != want {
t.Errorf("uniqueXorTriplets(%v) = %d, brute force = %d", in, got, want)
}
}
}