Implement transactional key–value store
Company: Applied Intuition
Role: Software Engineer
Category: Coding & Algorithms
Difficulty: Medium
Interview Round: Technical Screen
Quick Answer: This question evaluates transactional state management, nested-scope semantics, and mutable key–value data structures, testing competencies in data structures, algorithms, and complexity analysis within the Coding & Algorithms domain.
Constraints
- 1 <= len(commands) <= 50000
- Keys and values are single tokens with no spaces
- Transaction nesting can be arbitrarily deep within the command count limit
- All commands are valid and use one of: SET, GET, BEGIN, ROLLBACK, COMMIT
Examples
Input: (["GET a", "SET a 10", "GET a", "SET a 20", "GET a"],)
Expected Output: ["NULL", "10", "20"]
Explanation: The first GET sees no value. After SET commands, GET returns the latest visible value.
Input: (["SET a 10", "BEGIN", "GET a", "SET a 30", "GET a", "ROLLBACK", "GET a"],)
Expected Output: ["10", "30", "10"]
Explanation: Inside the transaction, a is updated to 30. After rollback, it returns to the outer value 10.
Input: (["SET a 10", "BEGIN", "SET a 20", "BEGIN", "SET b 30", "GET a", "GET b", "ROLLBACK", "GET b", "COMMIT", "GET a"],)
Expected Output: ["20", "30", "NULL", "20"]
Explanation: The inner rollback removes b, but a=20 from the outer transaction remains. COMMIT makes a=20 permanent.
Input: (["ROLLBACK", "COMMIT", "GET x"],)
Expected Output: ["NO TRANSACTION", "NO TRANSACTION", "NULL"]
Explanation: Both transaction operations are invalid with no active transaction, and x was never set.
Input: (["SET x 1", "BEGIN", "SET x 2", "SET x 3", "BEGIN", "SET x 4", "ROLLBACK", "GET x", "ROLLBACK", "GET x"],)
Expected Output: ["3", "1"]
Explanation: The inner rollback restores x from 4 back to 3. The outer rollback then restores the original base value 1.
Solution
def solution(commands):
current = {}
transactions = []
output = []
for command in commands:
parts = command.split()
op = parts[0]
if op == "SET":
key, value = parts[1], parts[2]
if transactions:
log = transactions[-1]
if key not in log:
if key in current:
log[key] = (True, current[key])
else:
log[key] = (False, None)
current[key] = value
elif op == "GET":
key = parts[1]
output.append(current.get(key, "NULL"))
elif op == "BEGIN":
transactions.append({})
elif op == "ROLLBACK":
if not transactions:
output.append("NO TRANSACTION")
else:
log = transactions.pop()
for key, (had_value, old_value) in log.items():
if had_value:
current[key] = old_value
else:
current.pop(key, None)
elif op == "COMMIT":
if not transactions:
output.append("NO TRANSACTION")
else:
transactions.clear()
return outputTime complexity: SET, GET, BEGIN, and COMMIT are O(1) average time. ROLLBACK is O(k), where k is the number of distinct keys changed in the most recent transaction.. Space complexity: O(U + C), where U is the number of currently visible keys and C is the number of distinct key changes recorded across all open transactions..
Hints
- Keep one dictionary for the currently visible values so GET can be answered in O(1) average time.
- For each transaction, record a key's old value only the first time that key is changed inside that transaction.