Design a rolling event tracker with ranges
Company: Databricks
Role: Software Engineer
Category: Coding & Algorithms
Difficulty: Medium
Interview Round: Technical Screen
Quick Answer: This question evaluates understanding of time-series data structures and streaming counters, testing algorithm design, space/time complexity analysis, correctness across edge cases, and handling of large-scale event tracking.
Constraints
- 1 <= len(operations) <= 200000
- 0 <= timestamp, currentTimestamp, startTimestamp, endTimestamp <= 10^18
- Timestamps in record operations are non-decreasing
- 1 <= windowSeconds <= 10^18 for count operations
- Multiple events may occur at the same timestamp
Examples
Input: ([("record", 10), ("record", 10), ("record", 12), ("count", 1, 10), ("count", 3, 12), ("countRange", 11, 12)],)
Expected Output: [2, 3, 1]
Explanation: There are two events at 10 and one at 12. [10,10] has 2 events, [10,12] has 3 events, and [11,12] has only the event at 12.
Input: ([("count", 5, 100), ("countRange", 7, 7), ("record", 8), ("countRange", 9, 8), ("count", 1, 8)],)
Expected Output: [0, 0, 0, 1]
Explanation: Queries before any record return 0. The range [9,8] is empty because start > end. After recording one event at 8, the 1-second window ending at 8 contains 1 event.
Input: ([("record", 1), ("record", 2), ("record", 2), ("record", 5), ("count", 4, 5), ("count", 10, 10), ("countRange", 2, 2), ("countRange", 3, 4)],)
Expected Output: [3, 4, 2, 0]
Explanation: The window [2,5] contains timestamps 2, 2, and 5. The large window [1,10] includes all 4 recorded events. The single-point range [2,2] contains 2 events, while [3,4] contains none.
Input: ([("record", 999999999999), ("record", 1000000000000), ("record", 1000000000000), ("count", 2, 1000000000000), ("countRange", 1000000000000, 1000000000000), ("countRange", 999999999998, 999999999998)],)
Expected Output: [3, 2, 0]
Explanation: This checks very large timestamps. The 2-second window ending at 1000000000000 covers 999999999999 and 1000000000000, so it includes all 3 recorded events.
Input: ([("record", 3), ("count", 5, 3), ("record", 7), ("countRange", 1, 6), ("record", 7), ("count", 1, 7), ("count", 3, 8)],)
Expected Output: [1, 1, 2, 2]
Explanation: The first query sees only the event at 3. The range [1,6] excludes later events at 7. After two events are recorded at 7, both the single-second query at 7 and the 3-second window ending at 8 return 2.
Input: ([("record", 1), ("record", 1), ("record", 2)],)
Expected Output: []
Explanation: There are no query operations, so the returned list is empty.
Solution
def solution(operations):
from bisect import bisect_left, bisect_right
times = []
prefix = []
results = []
def range_count(start, end):
if end < start or not times:
return 0
left = bisect_left(times, start)
right = bisect_right(times, end) - 1
if left >= len(times) or right < left:
return 0
return prefix[right] - (prefix[left - 1] if left > 0 else 0)
for op in operations:
kind = op[0]
if kind == "record":
t = op[1]
if times and times[-1] == t:
prefix[-1] += 1
else:
times.append(t)
prefix.append((prefix[-1] if prefix else 0) + 1)
elif kind == "count":
window_seconds, current_timestamp = op[1], op[2]
if window_seconds <= 0:
results.append(0)
else:
start = current_timestamp - window_seconds + 1
results.append(range_count(start, current_timestamp))
elif kind == "countRange":
start_timestamp, end_timestamp = op[1], op[2]
results.append(range_count(start_timestamp, end_timestamp))
else:
raise ValueError("Unknown operation type")
return results
Time complexity: record: O(1) amortized; count/countRange: O(log m), where m is the number of distinct recorded timestamps. Space complexity: O(m), where m is the number of distinct recorded timestamps.
Hints
- Because record timestamps never decrease, you can keep timestamps in sorted order without inserting in the middle. If the same timestamp repeats, merge it into the last bucket.
- To answer an inclusive range query quickly, find the first recorded timestamp >= start and the last recorded timestamp <= end, then use cumulative totals.