Skip to content

1899. Merge Triplets to Form Target Triplet (Medium)

Problem

Given a list of triplets and a target triplet, you may pick a subset and take element-wise max to produce a new triplet. Return true if you can produce exactly target.

Example

  • triplets = [[2,5,3],[1,8,4],[1,7,5]], target = [2,7,5]true ([2,5,3] + [1,7,5])
  • triplets = [[1,3,4],[2,5,8]], target = [2,5,8]true
  • triplets = [[3,4,5]], target = [2,5,8]false

LeetCode 1899 · Link · Medium

Try it yourself

idle

Starter code: this editor begins with intentional TODOs. Fill the function, run the embedded tests, then compare your solution with the worked approaches below.

Click Run Python to execute. First run downloads Python (~10 MB, cached after that).

Approach 1: Brute force, enumerate subsets

Check the element-wise max of every subset.

from itertools import combinations
def merge_triplets(triplets, target):
n = len(triplets)
for r in range(1, n + 1): # L1: every subset size
for subset in combinations(triplets, r): # L2: 2^n total subsets
mx = [0, 0, 0]
for t in subset:
for i in range(3):
if t[i] > mx[i]:
mx[i] = t[i]
if mx == list(target):
return True
return False

There are 2^n subsets; each costs O(n)O(n) to combine. Total O(n2n)O(n · 2^n). Skip past tiny inputs.

Complexity

  • Time: O(n2n)O(n · 2^n).
  • Space: O(n)O(n).

Approach 2: Greedy channel-wise (canonical)

A triplet (a, b, c) is “usable” iff every channel is ≤ target. If any channel exceeds target, including it pushes that channel past the answer and can’t be undone.

Among usable triplets, check that each of the three channels gets at least one triplet achieving that target channel.

def merge_triplets(triplets, target):
hit = [False, False, False] # L1: O(1)
for t in triplets: # L2: single pass, n iterations
if t[0] > target[0] or t[1] > target[1] or t[2] > target[2]: # L3: O(1)
continue
for i in range(3): # L4: O(1), constant 3 channels
if t[i] == target[i]:
hit[i] = True # L5: O(1)
return all(hit) # L6: O(1)

Where the time goes, line by line

Variables: n = len(triplets).

LinePer-call costTimes executedContribution
L1 (init)O(1)O(1)1O(1)O(1)
L2, L3, L4, L5 (scan)O(1)O(1)nO(n)O(n) ← dominates
L6 (all check)O(1)O(1)1O(1)O(1)

Each triplet is visited once; the inner channel loop is constant (always 3 channels).

Complexity

  • Time: O(n)O(n), driven by L2/L3/L4/L5 (single pass over all triplets).
  • Space: O(1)O(1).

Why greedy works

Element-wise max is monotone, once a channel equals the target, subsequent usable triplets can’t reduce it below target. So we just need one usable triplet per channel hitting the target value.

Try this approach:

idle
Click Run Python to execute. First run downloads Python (~10 MB, cached after that).

Approach 3: Early-exit variant

Same as Approach 2 with an early return when all three hits are set.

def merge_triplets_early(triplets, target):
hit = 0 # L1: O(1), bitmask
for t in triplets: # L2: single pass, n iterations
if t[0] > target[0] or t[1] > target[1] or t[2] > target[2]: # L3: O(1)
continue
for i in range(3): # L4: O(1)
if t[i] == target[i]:
hit |= 1 << i # L5: O(1)
if hit == 0b111: # L6: O(1) early exit
return True
return hit == 0b111

Where the time goes, line by line

Variables: n = len(triplets).

LinePer-call costTimes executedContribution
L2-L5 (scan)O(1)O(1)up to nO(n)O(n) ← dominates
L6 (early exit check)O(1)O(1)up to nO(n)O(n)

Same asymptotic complexity as Approach 2 with a constant-factor speedup on lucky inputs where all three channels are hit early.

Complexity

  • Same as Approach 2 with constant-factor speedup on lucky inputs.

Try this approach:

idle
Click Run Python to execute. First run downloads Python (~10 MB, cached after that).

Summary

ApproachTimeSpace
Enumerate subsetsexponentialO(n)O(n)
Greedy channel-wiseO(n)O(n)O(1)O(1)

The “filter then hit each dimension” greedy pattern generalizes to K channels.

Test cases

func mergeTriplets(triplets [][]int, target []int) bool {
hit := [3]bool{}
for _, t := range triplets {
if t[0] > target[0] || t[1] > target[1] || t[2] > target[2] { continue }
for i := 0; i < 3; i++ {
if t[i] == target[i] { hit[i] = true }
}
}
return hit[0] && hit[1] && hit[2]
}
  • Greedy Algorithms, the local choice pattern protected by an invariant about the best reachable future.
  • Array Scans, the linear pass habit of carrying just enough state while reading each item once.