Merge Triplets to Form Target Triplet
MedPrompt
A triplet is an array of three integers. You are given a 2D integer array triplets, where triplets[i] = [ai, bi, ci] describes the ith triplet. You are also given an integer array target = [x, y, z] that describes the triplet you want to obtain.
To obtain target, you may apply the following operation on triplets any number of times (possibly zero):
- Choose two indices (0-indexed)
iandj(i != j) and updatetriplets[j]to become[max(ai, aj), max(bi, bj), max(ci, cj)].- For example, if
triplets[i] = [2, 5, 3]andtriplets[j] = [1, 7, 5],triplets[j]will be updated to[max(2, 1), max(5, 7), max(3, 5)] = [2, 7, 5].
- For example, if
Return true if it is possible to obtain the target triplet [x, y, z] as an element of triplets, or false otherwise.
Example 1:
Input: triplets = [[2,5,3],[1,8,4],[1,7,5]], target = [2,7,5]
Output: true
Explanation: Perform the following operations:
- Choose the first and last triplets [[2,5,3],[1,8,4],[1,7,5]]. Update the last triplet to be [max(2,1), max(5,7), max(3,5)] = [2,7,5]. triplets = [[2,5,3],[1,8,4],[2,7,5]]
The target triplet [2,7,5] is now an element of triplets.Example 2:
Input: triplets = [[3,4,5],[4,5,6]], target = [3,2,5]
Output: false
Explanation: It is impossible to have [3,2,5] as an element because there is no 2 in any of the triplets.Example 3:
Input: triplets = [[2,5,3],[2,3,4],[1,2,5],[5,2,3]], target = [5,5,5]
Output: true
Explanation: Perform the following operations:
- Choose the first and third triplets [[2,5,3],[2,3,4],[1,2,5],[5,2,3]]. Update the third triplet to be [max(2,1), max(5,2), max(3,5)] = [2,5,5]. triplets = [[2,5,3],[2,3,4],[2,5,5],[5,2,3]].
- Choose the third and fourth triplets [[2,5,3],[2,3,4],[2,5,5],[5,2,3]]. Update the fourth triplet to be [max(2,5), max(5,2), max(5,3)] = [5,5,5]. triplets = [[2,5,3],[2,3,4],[2,5,5],[5,5,5]].
The target triplet [5,5,5] is now an element of triplets.
Constraints:
1 <= triplets.length <= 105triplets[i].length == target.length == 31 <= ai, bi, ci, x, y, z <= 1000
Approaches
2 approaches with complexity analysis and trade-offs.
This approach first filters the input triplets to keep only those that could potentially contribute to forming the target triplet. A triplet is a potential candidate only if each of its elements is less than or equal to the corresponding element in the target. After filtering, it merges all these candidate triplets to see if the final result matches the target.
Algorithm
-
- Create an empty list,
goodTriplets.
- Create an empty list,
-
- Iterate through each
tripletin the inputtriplets.
- Iterate through each
-
- If
triplet[0] <= target[0]ANDtriplet[1] <= target[1]ANDtriplet[2] <= target[2], addtriplettogoodTriplets.
- If
-
- Initialize a result triplet
res = [0, 0, 0].
- Initialize a result triplet
-
- Iterate through each
goodTripletingoodTriplets.
- Iterate through each
-
- Update
res:res[0] = max(res[0], goodTriplet[0]),res[1] = max(res[1], goodTriplet[1]),res[2] = max(res[2], goodTriplet[2]).
- Update
-
- Return
trueifresis equal totarget, otherwise returnfalse.
- Return
Walkthrough
The core idea is based on the property of the max operation. When we merge triplets, the values in the resulting triplet can only increase or stay the same. Therefore, any triplet that has an element greater than the corresponding element in the target triplet can never be part of a merge that results in the target. Such triplets are "invalid" and can be discarded.
The algorithm proceeds in two main phases:
- Filtering Phase: Iterate through the original
tripletsarray. Create a new list of "good" triplets, containing only those[a, b, c]for whicha <= target[0],b <= target[1], andc <= target[2]. - Merging Phase: If the list of good triplets is not empty, we can effectively merge all of them. The result of merging all good triplets would be a triplet
[max_a, max_b, max_c], wheremax_ais the maximum of all first elements of the good triplets, and so on. We can find these maximums by iterating through the list of good triplets. - Verification: Finally, we check if this
[max_a, max_b, max_c]is equal to thetargettriplet. If they are equal, it means we can form the target; otherwise, we cannot.
import java.util.ArrayList;import java.util.List; class Solution { public boolean mergeTriplets(int[][] triplets, int[] target) { List<int[]> goodTriplets = new ArrayList<>(); for (int[] t : triplets) { if (t[0] <= target[0] && t[1] <= target[1] && t[2] <= target[2]) { goodTriplets.add(t); } } int[] res = {0, 0, 0}; for (int[] t : goodTriplets) { res[0] = Math.max(res[0], t[0]); res[1] = Math.max(res[1], t[1]); res[2] = Math.max(res[2], t[2]); } return res[0] == target[0] && res[1] == target[1] && res[2] == target[2]; }}Complexity
Time
O(N), where N is the number of triplets. The first loop for filtering takes O(N) time. The second loop for merging takes O(M) time, where M is the number of good triplets (M <= N). So the total time complexity is O(N).
Space
O(M) or O(N) in the worst case, for storing the `goodTriplets` list, where N is the total number of triplets and M is the number of good triplets. In the worst case, all triplets are "good", so we need space proportional to the input size.
Trade-offs
Pros
Correctly solves the problem.
The logic is straightforward and easy to understand: filter then process.
Cons
Uses extra space to store the filtered triplets, which can be significant if the input array is large.
This is an optimized version of the greedy approach that uses a single pass over the input array and constant extra space. It identifies valid triplets and simultaneously checks if the necessary components to form the target (x, y, and z) have been found among these valid triplets.
Algorithm
-
- Initialize three boolean flags:
foundX = false,foundY = false,foundZ = false.
- Initialize three boolean flags:
-
- Let
target = [x, y, z].
- Let
-
- Iterate through each
triplet = [a, b, c]in the inputtriplets.
- Iterate through each
-
- Check if the triplet is "good":
a <= xANDb <= yANDc <= z.
- Check if the triplet is "good":
-
- If it is a good triplet:
- If
a == x, setfoundX = true. - If
b == y, setfoundY = true. - If
c == z, setfoundZ = true.
-
- After the loop, return
foundX && foundY && foundZ.
- After the loop, return
Walkthrough
This approach builds upon the same key insight: any triplet with an element larger than the corresponding target element is useless. However, instead of storing all "good" triplets, we can process them on the fly.
We need to find if it's possible to construct the target = [x, y, z]. This requires three things to be true simultaneously:
- There exists a good triplet
[x, b, c](whereb <= y,c <= z). - There exists a good triplet
[a, y, c](wherea <= x,c <= z). - There exists a good triplet
[a, b, z](wherea <= x,b <= y). Note that these could be the same triplet or different triplets. By merging them, we can achieve the target. For example, merging[x, b1, c1],[a2, y, c2], and[a3, b3, z](all good) results in[max(x, a2, a3), max(b1, y, b3), max(c1, c2, z)]which simplifies to[x, y, z]because alla_i <= x,b_i <= y,c_i <= z. So, the problem reduces to checking if we can find a good triplet that matches thexcomponent, a good one that matches theycomponent, and a good one that matches thezcomponent. We can track this using three boolean flags. We iterate through the triplets once. For each triplet, we first check if it's "good". If it is, we then check if any of its components match the corresponding target components and update our flags accordingly.
class Solution { public boolean mergeTriplets(int[][] triplets, int[] target) { boolean foundX = false; boolean foundY = false; boolean foundZ = false; int targetX = target[0]; int targetY = target[1]; int targetZ = target[2]; for (int[] triplet : triplets) { // This is a "good" triplet if no element exceeds the corresponding target element. if (triplet[0] <= targetX && triplet[1] <= targetY && triplet[2] <= targetZ) { // Check if this triplet helps us match any of the target's components. if (triplet[0] == targetX) { foundX = true; } if (triplet[1] == targetY) { foundY = true; } if (triplet[2] == targetZ) { foundZ = true; } } } return foundX && foundY && foundZ; }}Complexity
Time
O(N), where N is the number of triplets. We iterate through the `triplets` array only once.
Space
O(1). We only use a few boolean variables to track the state, regardless of the input size.
Trade-offs
Pros
Highly efficient in both time and space.
Solves the problem in a single pass.
Cons
The logic might be slightly less intuitive at first glance compared to the two-pass approach, as it combines filtering and checking in one step.
Solutions
Solution
class Solution {public boolean mergeTriplets(int[][] triplets, int[] target) { int x = target[0], y = target[1], z = target[2]; int d = 0, e = 0, f = 0; for (var t : triplets) { int a = t[0], b = t[1], c = t[2]; if (a <= x && b <= y && c <= z) { d = Math.max(d, a); e = Math.max(e, b); f = Math.max(f, c); } } return d == x && e == y && f == z; }}class Solution {public: bool mergeTriplets(vector<vector<int>> &triplets, vector<int> &target) { int x = target[0], y = target[1], z = target[2]; int d = 0, e = 0, f = 0; for (auto &t : triplets) { int a = t[0], b = t[1], c = t[2]; if (a <= x && b <= y && c <= z) { d = max(d, a); e = max(e, b); f = max(f, c); } } return d == x && e == y && f == z; }};class Solution: def mergeTriplets(self, triplets: List[List[int]], target: List[int]) -> bool: x, y, z = target d = e = f = 0 for a, b, c in triplets: if a <= x and b <= y and c <= z: d = max(d, a) e = max(e, b) f = max(f, c) return [d, e, f] == targetVideo 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.