Implement credit ledger with out-of-order timestamps
Company: OpenAI
Role: Software Engineer
Category: Coding & Algorithms
Difficulty: hard
Interview Round: Technical Screen
Quick Answer: This question evaluates the ability to implement stateful event processing with out-of-order timestamps, correct ledger/accounting semantics including timestamp ordering and explicit tie-breaking between adds and charges.
Constraints
- 1 <= len(requests) <= 20000
- user is a non-empty string
- 0 <= amount <= 10^9
- -10^9 <= timestamp <= 10^9
- Timestamps are not guaranteed to be ordered
- If multiple events share the same timestamp for a user, they must be applied in arrival order
Examples
Input: ([["GRANT","alice",10,5],["CHARGE","alice",4,7],["GET","alice",7],["CHARGE","alice",10,6],["GET","alice",7]],)
Expected Output: [6, 0]
Explanation: First GET sees grant@5 then charge4@7 => 6. Second GET also sees a new charge10@6, so at t=7: grant10 -> charge10 succeeds -> balance 0; charge4 then fails => 0.
Input: ([["GRANT","alice",5,10],["GRANT","bob",7,1],["CHARGE","bob",2,3],["GET","bob",2],["GET","bob",3],["GET","alice",9],["GET","alice",10]],)
Expected Output: [7, 5, 0, 5]
Explanation: bob@2 includes only grant7@1 => 7. bob@3 includes charge2@3 => 5. alice@9 excludes grant@10 => 0. alice@10 includes grant5 => 5.
Input: ([["CHARGE","u1",1,-1],["GET","u1",-1],["GRANT","u1",2,-2],["GET","u1",-1]],)
Expected Output: [0, 1]
Explanation: At first GET, only a charge exists and it fails => 0. After a later-arriving earlier grant@-2, recomputing at t=-1 gives 2 then charge1 succeeds => 1.
Input: ([["CHARGE","a",3,5],["GRANT","a",10,5],["GET","a",5],["GRANT","a",1,4],["GET","a",5]],)
Expected Output: [10, 11]
Explanation: At t=5, same-timestamp events are processed in arrival order: charge then grant, so charge fails and balance becomes 10. After adding grant1@4, at t=5: grant1 -> charge3 fails -> grant10 => 11.
Input: ([],)
Expected Output: []
Explanation: No GET requests, so the output is an empty list.
Solution
from collections import defaultdict
def solution(requests):
"""Simulate an out-of-order timestamp credit ledger.
requests: List of requests. Each request is:
- ["GRANT", user, amount, timestamp]
- ["CHARGE", user, amount, timestamp]
- ["GET", user, timestamp]
Returns: list of balances for each GET, in order.
"""
# user -> list of events (timestamp, arrival_index, type, amount)
events = defaultdict(list)
out = []
for idx, req in enumerate(requests):
op = req[0]
if op == "GET":
user, t = req[1], req[2]
user_events = events.get(user)
if not user_events:
out.append(0)
continue
applicable = [e for e in user_events if e[0] <= t]
applicable.sort(key=lambda x: (x[0], x[1])) # (timestamp, arrival order)
bal = 0
for ts, arrival, typ, amt in applicable:
if typ == "GRANT":
bal += amt
else: # CHARGE
if bal >= amt:
bal -= amt
out.append(bal)
elif op == "GRANT" or op == "CHARGE":
user, amount, t = req[1], req[2], req[3]
# Store with arrival index for stable tie-breaking.
events[user].append((t, idx, op, amount))
else:
raise ValueError(f"Unknown operation: {op}")
return out
Time complexity: Worst-case O(G * K log K), where G is the number of GET requests and K is the number of events for the queried user (due to sorting on each GET).. Space complexity: O(E) where E is the number of GRANT/CHARGE events stored..
Hints
- Cache all GRANT/CHARGE events seen so far per user; when you get a GET, recompute that user's balance by sorting applicable events by (timestamp, arrival_index).
- To handle equal timestamps correctly, store each event with its position in the input stream and use it as a stable tie-breaker when sorting.