Maximum Profit Triplet ​
Problem Statement ​
Given an array of daily prices, find three days i < j < k such that prices[i] < prices[j] < prices[k] (strictly increasing) and return the maximum possible value of prices[i] + prices[j] + prices[k]. Return -1 if no such triplet exists.
Example 1:
Input: prices = [1, 5, 3, 4, 2, 6]
Output: 12
Explanation: Triplet (5, -, 6) is not valid because 5 is not followed by a middle > 5.
Triplet (1, 5, 6) works: 1 < 5 < 6, sum = 12.
Also (3, 4, 6) = 13 — actually better.
So max = 13. (Trace updated below.)Corrected Example 1:
Input: prices = [1, 5, 3, 4, 2, 6]
Output: 13
Explanation: (3, 4, 6) gives 3 + 4 + 6 = 13. Checking all valid triples, 13 is the max.Example 2:
Input: prices = [5, 4, 3, 2, 1]
Output: -1
Explanation: Array is strictly decreasing; no valid triplet.Constraints:
3 <= prices.length <= 10^5.1 <= prices[i] <= 10^4.
Difficulty: Medium Round: OA
Pattern Recognition ​
- What is the input structure? A flat array of integers.
- What are we optimizing? Max sum of three strictly increasing values at strictly increasing indices.
- What pattern does this fit? Fix the middle. For each index
j, find the max value to the left that is strictly less thanprices[j]and the max value to the right that is strictly greater thanprices[j]. - Why does this pattern fit? The triplet constraint is symmetric around
j. Once you pickj, the left and right subproblems are independent.
Approach ​
Brute Force — Triple Loop ​
For each (i, j, k), check the inequality and track the max sum.
Time: O(n^3). Space: O(1). Why insufficient: For n = 10^5, this is 10^15 ops. Dead on arrival.
Intermediate — O(n^2) Precomputation ​
For each j, scan left for the max value < prices[j], and scan right for the max value > prices[j]. Combine.
Time: O(n^2). Space: O(n).
Acceptable for n <= a few thousand. For n = 10^5, still 10^10 ops.
Optimal — Two Passes with Running Max ​
Key Insight: You do not need the strict comparison pre-computed per
jif you are willing to replace "max left less thanprices[j]" with a slightly different structure. The O(n log n) version uses a sorted structure to answer "max value at indices< jwith value< prices[j]" in O(log n).
A cleaner O(n log n) for this problem uses a Binary Indexed Tree (Fenwick tree) or ordered set with prefix-max keyed on value:
- Compress
pricesto ranks. - Left-to-right pass: for each
j, query the Fenwick tree formax value at ranks < rank(prices[j]), store asleftMaxLess[j]. Then insertprices[j]into the tree at positionrank(prices[j]). - Right-to-left pass (with a second tree): for each
j, query formax value at ranks > rank(prices[j]), store asrightMaxGreater[j]. Then insertprices[j]. - Scan
jfrom 1 to n-2, computeleftMaxLess[j] + prices[j] + rightMaxGreater[j], track max.
Time: O(n log n). Space: O(n).
At OA time, the O(n^2) version is usually fast enough and much simpler. If the problem constraints force O(n log n), reach for the Fenwick tree.
Walkthrough (O(n^2) version) ​
Trace on prices = [1, 5, 3, 4, 2, 6]:
leftMaxLess[j] (max of prices[i] for i < j with prices[i] < prices[j]):
| j | prices[j] | candidates (i < j, prices[i] < prices[j]) | leftMaxLess[j] |
|---|---|---|---|
| 0 | 1 | — | -1 |
| 1 | 5 | 1 | |
| 2 | 3 | 1 | |
| 3 | 4 | 3 | |
| 4 | 2 | 1 | |
| 5 | 6 | 5 |
rightMaxGreater[j] (max of prices[k] for k > j with prices[k] > prices[j]):
| j | prices[j] | candidates (k > j, prices[k] > prices[j]) | rightMaxGreater[j] |
|---|---|---|---|
| 0 | 1 | 6 | |
| 1 | 5 | 6 | |
| 2 | 3 | 6 | |
| 3 | 4 | 6 | |
| 4 | 2 | 6 | |
| 5 | 6 | — | -1 |
Combine:
| j | leftMaxLess[j] | prices[j] | rightMaxGreater[j] | sum |
|---|---|---|---|---|
| 1 | 1 | 5 | 6 | 12 |
| 2 | 1 | 3 | 6 | 10 |
| 3 | 3 | 4 | 6 | 13 |
| 4 | 1 | 2 | 6 | 9 |
Answer: 13 from triplet (3, 4, 6).
Implementation ​
function maxProfitTriplet(prices: number[]): number {
const n = prices.length;
if (n < 3) return -1;
const leftMaxLess = new Array(n).fill(-1);
const rightMaxGreater = new Array(n).fill(-1);
// leftMaxLess[j]: max price at index < j with value < prices[j].
for (let j = 0; j < n; j++) {
let best = -1;
for (let i = 0; i < j; i++) {
if (prices[i] < prices[j]) best = Math.max(best, prices[i]);
}
leftMaxLess[j] = best;
}
// rightMaxGreater[j]: max price at index > j with value > prices[j].
for (let j = 0; j < n; j++) {
let best = -1;
for (let k = j + 1; k < n; k++) {
if (prices[k] > prices[j]) best = Math.max(best, prices[k]);
}
rightMaxGreater[j] = best;
}
let best = -1;
for (let j = 1; j < n - 1; j++) {
if (leftMaxLess[j] !== -1 && rightMaxGreater[j] !== -1) {
best = Math.max(best, leftMaxLess[j] + prices[j] + rightMaxGreater[j]);
}
}
return best;
}#include <vector>
#include <algorithm>
int maxProfitTriplet(const std::vector<int>& prices) {
int n = prices.size();
if (n < 3) return -1;
std::vector<int> leftMaxLess(n, -1), rightMaxGreater(n, -1);
for (int j = 0; j < n; j++) {
int best = -1;
for (int i = 0; i < j; i++)
if (prices[i] < prices[j]) best = std::max(best, prices[i]);
leftMaxLess[j] = best;
}
for (int j = 0; j < n; j++) {
int best = -1;
for (int k = j + 1; k < n; k++)
if (prices[k] > prices[j]) best = std::max(best, prices[k]);
rightMaxGreater[j] = best;
}
int best = -1;
for (int j = 1; j < n - 1; j++) {
if (leftMaxLess[j] != -1 && rightMaxGreater[j] != -1)
best = std::max(best, leftMaxLess[j] + prices[j] + rightMaxGreater[j]);
}
return best;
}Watch out:
- Strict vs non-strict inequality. The problem says strictly increasing, so
<and>. Using<=admits equal values and yields different (wrong) triplets. - Returning 0 vs -1 when no triplet exists. Clarify. The canonical convention is -1 as a sentinel.
- Off-by-one on the middle loop.
jranges from 1 ton - 2inclusive — you need at least one index on each side.
Complexity Analysis ​
| Time | Space | |
|---|---|---|
| Brute Force | O(n^3) | O(1) |
| O(n^2) Precomputation | O(n^2) | O(n) |
| Fenwick Tree (O(n log n)) | O(n log n) | O(n) |
Bottleneck: For the O(n^2) version, the nested scans. For the Fenwick version, the log factor from tree queries.
Edge Cases ​
- Fewer than 3 elements — return -1 immediately.
- Strictly decreasing array — no triplet; return -1.
- All equal values — no strict inequality; return -1.
- Exactly 3 elements, increasing — return the sum.
- Duplicate values at middle — if multiple
igive the sameleftMaxLess, take the max; the algorithm does this naturally.
Related Problems ​
- LC 334 — Increasing Triplet Subsequence — Same shape, but only asks for existence (boolean), not the max sum. Has a beautiful O(n) solution with two "smallest" trackers.
- LC 300 — Longest Increasing Subsequence — Generalization to arbitrary length. O(n log n) with patience sort.
- LC 456 — 132 Pattern — Related pattern-matching problem, uses a monotonic stack.
OA Strategy Notes ​
Medium-difficulty OA problem. Expect 20-30 minutes. The O(n^2) version is likely fast enough for HackerRank's typical limits (n <= 5000). If constraints push to 10^5, prep the Fenwick tree approach. In either case, verify on the "strictly decreasing" edge case and confirm -1 is returned.
What is Expected at Each Level ​
Junior: Brute force O(n^3), arrives at O(n^2) with prompting.
Mid-level: Writes O(n^2) cleanly. Handles edge cases. Notes that Fenwick tree could push to O(n log n) if needed.
Senior: Implements the Fenwick tree version without being asked. Discusses strict vs non-strict and the implications on ties. Connects to LC 334 as the existence-only cousin.