Design hit counter for last 5 minutes
Company: Vercel
Role: Software Engineer
Category: Coding & Algorithms
Difficulty: medium
Interview Round: Technical Screen
Quick Answer: This question evaluates competency in designing time-windowed counting mechanisms, efficient data structures for time-series events, and algorithmic performance under real-time constraints.
Part 1: Hit Counter for the Last 5 Minutes
Constraints
- 0 <= len(operations) == len(timestamps) <= 200000
- Each operation is either `track_hit` or `get_hit_count_in_last_5_minutes`
- 0 <= timestamps[i] <= 10^9
- Timestamps are non-decreasing
Examples
Input: (['track_hit', 'track_hit', 'track_hit', 'get_hit_count_in_last_5_minutes', 'track_hit', 'get_hit_count_in_last_5_minutes'], [1, 2, 300, 300, 301, 301])
Expected Output: [3, 3]
Explanation: At time 300, hits at 1, 2, and 300 are counted. At time 301, the hit at 1 expires, but the new hit at 301 is added, so the count is still 3.
Input: (['track_hit', 'track_hit', 'track_hit', 'get_hit_count_in_last_5_minutes', 'get_hit_count_in_last_5_minutes'], [10, 10, 10, 10, 309])
Expected Output: [3, 3]
Explanation: Multiple hits can happen at the same second. At time 309, hits from time 10 are still inside the window because 309 - 299 = 10.
Input: (['track_hit', 'track_hit', 'get_hit_count_in_last_5_minutes', 'get_hit_count_in_last_5_minutes'], [1, 300, 300, 301])
Expected Output: [2, 1]
Explanation: At time 300, both hits count. At time 301, the hit at time 1 is now outside the last 300 seconds.
Input: (['get_hit_count_in_last_5_minutes'], [50])
Expected Output: [0]
Explanation: No hits were tracked before the query.
Input: ([], [])
Expected Output: []
Explanation: Empty input should return an empty list.
Hints
- Only hits from the last 300 seconds matter. Older hits can be discarded permanently.
- Because timestamps are non-decreasing, each stored hit is added once and removed once.
Part 2: Memory-Optimized Hit Counter with Fixed-Size Buckets
Constraints
- 0 <= len(operations) == len(timestamps) <= 1000000
- Each operation is either `track_hit` or `get_hit_count_in_last_5_minutes`
- 0 <= timestamps[i] <= 10^9
- Timestamps are non-decreasing
- Your approach should avoid storing one item per individual hit
Examples
Input: (['track_hit', 'track_hit', 'track_hit', 'track_hit', 'track_hit', 'get_hit_count_in_last_5_minutes', 'get_hit_count_in_last_5_minutes', 'track_hit', 'get_hit_count_in_last_5_minutes'], [100, 100, 100, 100, 100, 100, 399, 400, 400])
Expected Output: [5, 5, 1]
Explanation: Five hits happen at time 100. They still count at time 399, but they expire by time 400; after tracking one new hit at 400, the count is 1.
Input: (['track_hit', 'track_hit', 'get_hit_count_in_last_5_minutes'], [1, 301, 301])
Expected Output: [1]
Explanation: Both timestamps map to the same bucket index, so the older value must be overwritten when time 301 arrives.
Input: (['track_hit', 'track_hit', 'get_hit_count_in_last_5_minutes', 'get_hit_count_in_last_5_minutes'], [42, 42, 341, 342])
Expected Output: [2, 0]
Explanation: At time 341, hits at 42 are still included because 341 - 299 = 42. At time 342, they expire.
Input: (['track_hit', 'track_hit', 'track_hit', 'track_hit', 'track_hit', 'track_hit', 'get_hit_count_in_last_5_minutes', 'get_hit_count_in_last_5_minutes'], [1, 2, 2, 300, 300, 300, 300, 301])
Expected Output: [6, 5]
Explanation: At time 300, all six hits are within the window. At time 301, the hit at time 1 expires, leaving five.
Input: ([], [])
Expected Output: []
Explanation: Empty input should return an empty list.
Hints
- Use exactly 300 buckets, one for each possible second offset in the sliding window.
- Each bucket should store both a timestamp and a hit count so you can tell whether the bucket still belongs to the current window or needs to be reset.