You are given a 0-indexed array nums
of size n
consisting of positive integers.
You are also given a 2D array queries
of size m
where queries[i] = [indexi, ki]
.
Initially all elements of the array are unmarked.
You need to apply m
queries on the array in order, where on the ith
query you do the following:
indexi
if it is not already marked.ki
unmarked elements in the array with the smallest values. If multiple such elements exist, mark the ones with the smallest indices. And if less than ki
unmarked elements exist, then mark all of them.Return an array answer of size m
where answer[i]
is the sum of unmarked elements in the array after the ith
query.
Example 1:
Input: nums = [1,2,2,1,2,3,1], queries = [[1,2],[3,3],[4,2]]
Output: [8,3,0]
Explanation:
We do the following queries on the array:
1
, and 2
of the smallest unmarked elements with the smallest indices if they exist, the marked elements now are nums = [1,2,2,1,2,3,1]
. The sum of unmarked elements is 2 + 2 + 3 + 1 = 8
.3
, since it is already marked we skip it. Then we mark 3
of the smallest unmarked elements with the smallest indices, the marked elements now are nums = [1,2,2,1,2,3,1]
. The sum of unmarked elements is 3
.4
, since it is already marked we skip it. Then we mark 2
of the smallest unmarked elements with the smallest indices if they exist, the marked elements now are nums = [1,2,2,1,2,3,1]
. The sum of unmarked elements is 0
.Example 2:
Input: nums = [1,4,2,3], queries = [[0,1]]
Output: [7]
Explanation: We do one query which is mark the element at index 0
and mark the smallest element among unmarked elements. The marked elements will be nums = [1,4,2,3]
, and the sum of unmarked elements is 4 + 3 = 7
.
Constraints:
n == nums.length
m == queries.length
1 <= m <= n <= 105
1 <= nums[i] <= 105
queries[i].length == 2
0 <= indexi, ki <= n - 1
When you get asked this question in a real-life environment, it will often be ambiguous (especially at FAANG). Make sure to ask these questions in that case:
Imagine you have a list of light switches and a set of instructions to flip certain switches on or off multiple times. The brute force method involves directly following each instruction, one at a time, in the order they are given, to see which switches end up on.
Here's how the algorithm would work step-by-step:
def mark_elements_brute_force(array_length, queries):
# Initialize all elements to 'off' (False).
element_marks = [False] * array_length
# Iterate through each query.
for query in queries:
# Iterate through each element in the array.
for index in range(array_length):
# If the index is within the query range, flip the state.
if query[0] <= index <= query[1]:
element_marks[index] = not element_marks[index]
# Collect indices with elements marked 'on' (True)
marked_indices = [index for index, marked in enumerate(element_marks) if marked]
return marked_indices
The best way to solve this problem is to keep track of which parts of the collection have been marked. We'll use a special technique that allows us to efficiently update large sections at once, instead of checking each spot individually. This speeds up the entire process considerably.
Here's how the algorithm would work step-by-step:
class Segment:
def __init__(self, start_index, end_index, marked_status):
self.start_index = start_index
self.end_index = end_index
self.marked_status = marked_status
def mark_elements(array_length, queries):
segments = [Segment(0, array_length - 1, False)]
for start_index, end_index in queries:
new_segments = []
for segment in segments:
# If the segment is outside the query range, keep it as is.
if segment.end_index < start_index or segment.start_index > end_index:
new_segments.append(segment)
continue
# If the segment is fully contained, update the marked status.
if start_index <= segment.start_index and segment.end_index <= end_index:
new_segments.append(Segment(segment.start_index, segment.end_index, True))
continue
# This handles partial overlaps. We need to split the segment.
if segment.start_index < start_index:
new_segments.append(Segment(segment.start_index, start_index - 1, segment.marked_status))
if end_index < segment.end_index:
new_segments.append(Segment(end_index + 1, segment.end_index, segment.marked_status))
# Mark the overlapping portion as true.
new_start = max(segment.start_index, start_index)
new_end = min(segment.end_index, end_index)
new_segments.append(Segment(new_start, new_end, True))
segments = new_segments
marked_array = [False] * array_length
# Build the final marked array by querying the segments.
for i in range(array_length):
for segment in segments:
if segment.start_index <= i <= segment.end_index:
marked_array[i] = segment.marked_status
break
return marked_array
Case | How to Handle |
---|---|
nums is null or empty | Return 0, as there are no elements to mark. |
queries is null or empty | Return 0, as no queries can mark elements. |
nums array contains duplicate values but only one query matches a duplicate value's index. | The solution should correctly mark only the element at the specified index when the query matches. |
Queries contain duplicate index-value pairs. | The solution should process each query, but subsequent identical queries should not affect the marked status if the element at that index is already marked. |
Queries contain the same index but different values; only the last matching query should matter. | The solution should ensure that the last query that matches index and value determines whether the element is marked. |
nums array contains negative numbers and queries use negative values | The solution should handle negative numbers without issues. |
Index in queries array is out of bounds (less than 0 or greater than or equal to nums.length) | The solution should skip queries with out-of-bounds indexes, or treat them as invalid without crashing. |
Large input array and large number of queries to test efficiency. | The solution should iterate through the queries in O(m) time (where m is the number of queries), and marking the array elements should be O(1) lookup which achieves reasonable performance. |