Design timestamped key-value map
Company: Character.AI
Role: Software Engineer
Category: Coding & Algorithms
Difficulty: Medium
Interview Round: Technical Screen
Quick Answer: This question evaluates the ability to design a timestamped key-value data structure, testing competencies in time-based indexing, ordered retrieval, and data structure selection for efficient timestamp queries.
Constraints
- 1 <= len(operations) <= 200000
- Each operation is either ["insert", key:str, value:str, timestamp:int] or ["get", key:str, timestamp:int]
- All insert operations appear in nondecreasing timestamp order (timestamps may be equal)
- 0 <= timestamp <= 1e9
- 1 <= len(key), len(value) <= 50
- The function returns results for get operations only; insert operations produce no direct output
Solution
from bisect import bisect_left
def time_ceiling_kv(operations: list[list]) -> list:
"""
Process a sequence of ["insert", key, value, timestamp] and ["get", key, timestamp].
Returns a list with results for get operations; missing results are None.
Assumes all inserts are given in nondecreasing timestamp order.
"""
store = {} # key -> (timestamps:list[int], values:list[str])
out = []
for op in operations:
if not op:
continue
cmd = op[0]
if cmd == "insert":
if len(op) != 4:
raise ValueError("insert expects 4 fields: ['insert', key, value, timestamp]")
_, k, v, t = op
pair = store.get(k)
if pair is None:
store[k] = ([t], [v])
else:
ts, vs = pair
ts.append(t) # valid due to nondecreasing timestamp assumption
vs.append(v)
elif cmd == "get":
if len(op) != 3:
raise ValueError("get expects 3 fields: ['get', key, timestamp]")
_, k, t = op
pair = store.get(k)
if pair is None:
out.append(None)
else:
ts, vs = pair
i = bisect_left(ts, t) # first index with ts[i] >= t
if i < len(ts):
out.append(vs[i])
else:
out.append(None)
else:
raise ValueError(f"unknown operation: {cmd}")
return out
Explanation
Time complexity: Insert: O(1) amortized per operation (append); Get: O(log m) per query where m is the number of entries for the key. Space complexity: O(N) where N is the total number of inserted entries.
Hints
- Map each key to a sorted list of timestamps and a parallel list of values.
- Because inserts are in nondecreasing timestamp order, you can append to each key's lists to keep them sorted.
- Use binary search (bisect_left) on the timestamps list to find the first index with timestamp >= query.
- If inserts were out of order, maintain each key's timestamps in sorted order via bisect.insort or use a balanced BST.