Track article votes and last three flips
Company: Rippling
Role: Software Engineer
Category: Coding & Algorithms
Difficulty: medium
Interview Round: Onsite
Quick Answer: This question evaluates a developer's ability to design in-memory data structures that support constant-time vote updates, score aggregation, and recent flip-event tracking while handling vote-change semantics.
Constraints
- 0 <= len(operations) <= 2 * 10^5
- Each operation is either ["vote", user_id, article_id, new_vote], ["getScore", article_id], or ["getLast3Flips", article_id]
- user_id and article_id are non-empty strings, and new_vote is one of {"UP", "DOWN", "NONE"}
- Your approach should run in O(1) average time per operation
Examples
Input: [['vote', 'u1', 'a1', 'UP'], ['vote', 'u2', 'a1', 'DOWN'], ['getScore', 'a1'], ['vote', 'u1', 'a1', 'DOWN'], ['getLast3Flips', 'a1'], ['vote', 'u2', 'a1', 'UP'], ['vote', 'u3', 'a1', 'UP'], ['vote', 'u3', 'a1', 'DOWN'], ['getScore', 'a1'], ['getLast3Flips', 'a1']]
Expected Output: [0, [['u1', 'UP', 'DOWN', 1]], -1, [['u1', 'UP', 'DOWN', 1], ['u2', 'DOWN', 'UP', 2], ['u3', 'UP', 'DOWN', 3]]]
Explanation: The first score is 0 because one user voted UP and one voted DOWN. Then three flips happen globally on the same article with sequence numbers 1, 2, and 3. The final score is -1.
Input: [['vote', 'u1', 'a2', 'DOWN'], ['getScore', 'a2'], ['getLast3Flips', 'a2'], ['vote', 'u1', 'a2', 'NONE'], ['getScore', 'a2'], ['vote', 'u1', 'a2', 'DOWN'], ['vote', 'u1', 'a2', 'UP'], ['getLast3Flips', 'a2']]
Expected Output: [-1, [], 0, [['u1', 'DOWN', 'UP', 1]]]
Explanation: Changing from DOWN to NONE is not a flip. After voting DOWN again and then UP, there is exactly one flip event.
Input: [['vote', 'u1', 'a1', 'UP'], ['vote', 'u1', 'a1', 'UP'], ['vote', 'u1', 'a1', 'NONE'], ['vote', 'u1', 'a1', 'NONE'], ['getScore', 'a1'], ['getLast3Flips', 'a1'], ['getScore', 'missing'], ['getLast3Flips', 'missing']]
Expected Output: [0, [], 0, []]
Explanation: Repeatedly voting the same value does nothing. Moving to or from NONE is not a flip. Unseen articles return score 0 and no flips.
Input: [['vote', 'u1', 'a1', 'UP'], ['vote', 'u1', 'a1', 'DOWN'], ['vote', 'u2', 'a2', 'DOWN'], ['vote', 'u2', 'a2', 'UP'], ['vote', 'u3', 'a1', 'DOWN'], ['vote', 'u3', 'a1', 'UP'], ['vote', 'u4', 'a1', 'UP'], ['vote', 'u4', 'a1', 'DOWN'], ['vote', 'u5', 'a1', 'DOWN'], ['vote', 'u5', 'a1', 'UP'], ['getLast3Flips', 'a1'], ['getLast3Flips', 'a2'], ['getScore', 'a1'], ['getScore', 'a2']]
Expected Output: [[['u3', 'DOWN', 'UP', 3], ['u4', 'UP', 'DOWN', 4], ['u5', 'DOWN', 'UP', 5]], [['u2', 'DOWN', 'UP', 2]], 0, 1]
Explanation: Flip sequence numbers are global across both articles. Article a1 has 4 flips total, so only its last 3 are returned: sequences 3, 4, and 5.
Input: []
Expected Output: []
Explanation: No operations means there are no query results.
Hints
- Map votes to numbers: UP = +1, DOWN = -1, NONE = 0. Then a vote update changes the score by value(new_vote) - value(old_vote).
- Store the current vote for each (user_id, article_id) pair, and keep a fixed-size queue/deque of at most 3 flip events per article.