Detect n-length consecutive sequences
Company: Anthropic
Role: Software Engineer
Category: Coding & Algorithms
Difficulty: Medium
Interview Round: Technical Screen
Quick Answer: This question evaluates a candidate's competency in algorithm design and data-structure reasoning for detecting n-length consecutive integer sequences, encompassing handling of duplicates, negative values, and dynamic insertions and deletions.
Part 1: Detect a consecutive run in an unsorted distinct array
Constraints
- 0 <= len(nums) <= 100000
- 0 <= nums[i] <= 10^9
- All values in nums are distinct
- 1 <= n <= 100000
Examples
Input: ([8, 1, 4, 3, 2], 4)
Expected Output: [1, 2, 3, 4]
Explanation: The numbers 1, 2, 3, and 4 are all present.
Input: ([10, 6, 7, 8, 20], 3)
Expected Output: [6, 7, 8]
Explanation: The array contains one run of length 3 starting at 6.
Input: ([5, 9, 1], 2)
Expected Output: []
Explanation: No two distinct values form a consecutive pair.
Input: ([], 1)
Expected Output: []
Explanation: An empty array cannot contain any run.
Input: ([42], 1)
Expected Output: [42]
Explanation: Any single value forms a consecutive run of length 1.
Solution
def solution(nums, n):
if n <= 0 or not nums or n > len(nums):
return []
nums = sorted(nums)
if n == 1:
return [nums[0]]
streak_len = 1
for i in range(1, len(nums)):
if nums[i] == nums[i - 1] + 1:
streak_len += 1
else:
streak_len = 1
if streak_len >= n:
end = nums[i]
start = end - n + 1
return list(range(start, end + 1))
return []
Time complexity: O(m log m), where m = len(nums). Space complexity: O(m).
Hints
- Sorting the numbers can turn the problem into finding a long enough consecutive streak.
- Because all values are distinct in this version, you only need to compare adjacent sorted elements.
Part 2: Detect a consecutive run with duplicates and negative numbers
Constraints
- 0 <= len(nums) <= 100000
- -10^9 <= nums[i] <= 10^9
- Duplicates may appear in nums
- 1 <= n <= 100000
Examples
Input: ([-2, -1, -1, 0, 4, 5], 3)
Expected Output: [-2, -1, 0]
Explanation: After ignoring duplicates, -2, -1, and 0 form a valid run.
Input: ([3, 3, 2, 1, 10], 3)
Expected Output: [1, 2, 3]
Explanation: The duplicate 3 should count only once.
Input: ([5, 4, 4, -1, 0, 1], 2)
Expected Output: [-1, 0]
Explanation: Several runs exist, and the smallest starting value is -1.
Input: ([-5, -3, -3, -1], 2)
Expected Output: []
Explanation: No two distinct consecutive integers exist.
Input: ([], 4)
Expected Output: []
Explanation: An empty input cannot contain a run.
Solution
def solution(nums, n):
if n <= 0 or not nums:
return []
values = sorted(set(nums))
if not values or n > len(values):
return []
if n == 1:
return [values[0]]
streak_len = 1
for i in range(1, len(values)):
if values[i] == values[i - 1] + 1:
streak_len += 1
else:
streak_len = 1
if streak_len >= n:
end = values[i]
start = end - n + 1
return list(range(start, end + 1))
return []
Time complexity: O(m log m), where m = len(nums). Space complexity: O(m).
Hints
- Duplicates should be ignored when deciding whether values are consecutive.
- After removing duplicates, a sorted scan works even when numbers are negative.
Part 3: Find a consecutive run in near-linear time
Constraints
- 0 <= len(nums) <= 200000
- -10^9 <= nums[i] <= 10^9
- Duplicates may appear in nums
- 1 <= n <= 200000
Examples
Input: ([100, 4, 200, 1, 3, 2], 4)
Expected Output: [1, 2, 3, 4]
Explanation: The classic consecutive run 1 through 4 is present.
Input: ([10, 11, 12, 1, 2, 3, 4], 3)
Expected Output: [1, 2, 3]
Explanation: Both 1-3 and 10-12 work, so return the smaller start.
Input: ([-2, -1, 0, 50, 51, 52], 3)
Expected Output: [-2, -1, 0]
Explanation: Negative values work exactly the same as positive ones.
Input: ([7, 7, 7], 2)
Expected Output: []
Explanation: Only one distinct value is present.
Input: ([9, 2, 5], 1)
Expected Output: [2]
Explanation: For n = 1, return the smallest present value.
Solution
def solution(nums, n):
if n <= 0 or not nums:
return []
values = set(nums)
if not values or n > len(values):
return []
best_start = None
for x in values:
if x - 1 in values:
continue
y = x
while y in values:
y += 1
if y - x >= n and (best_start is None or x < best_start):
best_start = x
if best_start is None:
return []
return list(range(best_start, best_start + n))
Time complexity: O(m) average, where m = len(nums). Space complexity: O(m).
Hints
- A hash set gives O(1) average membership checks.
- Only try to grow a run from values whose predecessor is not present.
Part 4: Maintain consecutive runs in an online stream
Constraints
- 1 <= n <= 100000
- 0 <= len(operations) <= 20000
- -10^9 <= value <= 10^9
- Delete on a value not currently present should be ignored
- The stream is a multiset: a value disappears from the distinct set only when its count drops to 0
Examples
Input: (3, [[1, 5], [1, 1], [1, 2], [1, 3], [3, 0], [1, 10], [1, 11], [1, 12], [3, 0]])
Expected Output: [[1, 2, 3], [1, 2, 3]]
Explanation: After the first four inserts, 1-3 exists. Later 10-12 also exists, but 1-3 has the smaller start.
Input: (3, [[1, -1], [1, 0], [1, 1], [1, 1], [3, 0], [2, 0], [3, 0], [2, 1], [3, 0], [1, 0], [3, 0]])
Expected Output: [[-1, 0, 1], [], [], [-1, 0, 1]]
Explanation: Duplicates are counted, so deleting one 1 does not remove the value 1 entirely.
Input: (1, [[3, 0], [1, 2], [3, 0], [1, 2], [2, 2], [3, 0], [2, 2], [3, 0]])
Expected Output: [[], [2], [2], []]
Explanation: For n = 1, any active distinct value is enough.
Input: (4, [[1, 1], [1, 2], [1, 4], [1, 5], [3, 0], [1, 3], [3, 0], [2, 10], [3, 0], [2, 2], [3, 0]])
Expected Output: [[], [1, 2, 3, 4], [1, 2, 3, 4], []]
Explanation: Inserting 3 bridges two intervals into 1-5, and deleting 2 later breaks the length-4 run.
Solution
def solution(n, operations):
from bisect import bisect_left, bisect_right, insort
import heapq
counts = {}
starts = []
start_to_end = {}
end_to_start = {}
heap = []
def add_start(s):
insort(starts, s)
def remove_start(s):
i = bisect_left(starts, s)
if i < len(starts) and starts[i] == s:
starts.pop(i)
def push_if_valid(s):
e = start_to_end.get(s)
if e is not None and e - s + 1 >= n:
heapq.heappush(heap, s)
def insert_value(x):
counts[x] = counts.get(x, 0) + 1
if counts[x] > 1:
return
left_start = end_to_start.get(x - 1)
right_end = start_to_end.get(x + 1)
if left_start is None and right_end is None:
start_to_end[x] = x
end_to_start[x] = x
add_start(x)
push_if_valid(x)
elif left_start is not None and right_end is None:
old_end = x - 1
start_to_end[left_start] = x
del end_to_start[old_end]
end_to_start[x] = left_start
push_if_valid(left_start)
elif left_start is None and right_end is not None:
old_start = x + 1
end = right_end
del start_to_end[old_start]
start_to_end[x] = end
end_to_start[end] = x
remove_start(old_start)
add_start(x)
push_if_valid(x)
else:
old_left_end = x - 1
old_right_start = x + 1
right_end_value = start_to_end[old_right_start]
start_to_end[left_start] = right_end_value
del end_to_start[old_left_end]
del start_to_end[old_right_start]
end_to_start[right_end_value] = left_start
remove_start(old_right_start)
push_if_valid(left_start)
def delete_value(x):
current = counts.get(x, 0)
if current == 0:
return
if current > 1:
counts[x] = current - 1
return
del counts[x]
i = bisect_right(starts, x) - 1
if i < 0:
return
s = starts[i]
e = start_to_end[s]
if x < s or x > e:
return
if s == e == x:
del start_to_end[s]
del end_to_start[e]
remove_start(s)
elif x == s:
new_s = s + 1
del start_to_end[s]
start_to_end[new_s] = e
end_to_start[e] = new_s
remove_start(s)
add_start(new_s)
push_if_valid(new_s)
elif x == e:
new_e = e - 1
start_to_end[s] = new_e
del end_to_start[e]
end_to_start[new_e] = s
push_if_valid(s)
else:
left_end = x - 1
right_start = x + 1
start_to_end[s] = left_end
end_to_start[left_end] = s
start_to_end[right_start] = e
end_to_start[e] = right_start
add_start(right_start)
push_if_valid(s)
push_if_valid(right_start)
def query():
while heap:
s = heap[0]
e = start_to_end.get(s)
if e is None or e - s + 1 < n:
heapq.heappop(heap)
else:
return list(range(s, s + n))
return []
answers = []
for op in operations:
typ = op[0]
value = op[1] if len(op) > 1 else 0
if typ == 1:
insert_value(value)
elif typ == 2:
delete_value(value)
elif typ == 3:
answers.append(query())
return answers
Time complexity: Insert/Delete: O(I) due to ordered interval-start maintenance with a Python list; Query: O(log I) amortized with lazy heap cleanup, where I is the number of current intervals. Space complexity: O(U + Q), where U is the number of currently distinct active values and Q is the number of operations.
Hints
- Think in terms of maximal consecutive intervals of currently active distinct values.
- Keep a count for each value so duplicates only affect the structure when a count changes between 0 and 1.