Implement an article voting tracker
Company: Rippling
Role: Software Engineer
Category: Coding & Algorithms
Difficulty: easy
Interview Round: Technical Screen
Quick Answer: This question evaluates a candidate's ability to design efficient stateful data structures and algorithms for tracking user votes, including correct handling of idempotent repeated votes, vote changes, deduplication, and retrieval of the most recent events.
Constraints
- 0 <= len(operations) <= 200000
- voteType is always 'UP' or 'DOWN'
- Each operation should be handled efficiently, ideally in O(1) average time
Examples
Input: [('vote', 'u1', 'a1', 'UP'), ('vote', 'u1', 'a1', 'UP'), ('vote', 'u1', 'a2', 'DOWN'), ('vote', 'u1', 'a1', 'DOWN'), ('getLastVotes', 'u1')]
Expected Output: [[('a1', 'DOWN', 3), ('a2', 'DOWN', 2), ('a1', 'UP', 1)]]
Explanation: The second vote is a duplicate and is ignored. The change from UP to DOWN on a1 creates a new event.
Input: [('vote', 'u1', 'a1', 'UP'), ('vote', 'u2', 'a2', 'DOWN'), ('getLastVotes', 'u3'), ('getLastVotes', 'u2'), ('getLastVotes', 'u1')]
Expected Output: [[], [('a2', 'DOWN', 2)], [('a1', 'UP', 1)]]
Explanation: u3 has no history, so the result is empty. u2 and u1 each have only one counted vote event.
Input: [('vote', 'u1', 'a1', 'UP'), ('vote', 'u1', 'a2', 'UP'), ('vote', 'u1', 'a3', 'DOWN'), ('vote', 'u1', 'a4', 'UP'), ('vote', 'u1', 'a2', 'UP'), ('getLastVotes', 'u1')]
Expected Output: [[('a4', 'UP', 4), ('a3', 'DOWN', 3), ('a2', 'UP', 2)]]
Explanation: Only the last 3 counted events are returned. The repeated UP vote on a2 is ignored.
Input: [('vote', 'u1', 'a1', 'UP'), ('vote', 'u1', 'a1', 'DOWN'), ('vote', 'u1', 'a1', 'UP'), ('getLastVotes', 'u1')]
Expected Output: [[('a1', 'UP', 3), ('a1', 'DOWN', 2), ('a1', 'UP', 1)]]
Explanation: Each change of vote on the same article creates a new counted event.
Input: []
Expected Output: []
Explanation: With no operations, there are no query results.
Solution
from collections import defaultdict, deque
def solution(operations):
current_votes = defaultdict(dict)
recent_events = defaultdict(lambda: deque(maxlen=3))
event_order = 0
results = []
for op in operations:
if op[0] == 'vote':
_, user_id, article_id, vote_type = op
previous = current_votes[user_id].get(article_id)
if previous == vote_type:
continue
event_order += 1
current_votes[user_id][article_id] = vote_type
recent_events[user_id].append((article_id, vote_type, event_order))
elif op[0] == 'getLastVotes':
_, user_id = op
results.append(list(reversed(recent_events.get(user_id, []))))
else:
raise ValueError(f'Unknown operation: {op[0]}')
return resultsTime complexity: O(m) total for m operations, with O(1) average time per operation. Space complexity: O(p + u), where p is the number of distinct (userId, articleId) pairs that have a stored current vote and u is the number of users with recent history.
Hints
- Keep the current vote for each (userId, articleId) pair so you can detect whether a new vote is a duplicate or a change.
- Since each query only needs the last 3 events, you only need to keep a small recent history per user.