Compute account balances with rejection and overdraft
Company: Stripe
Role: Software Engineer
Category: Coding & Algorithms
Difficulty: medium
Interview Round: Onsite
Quick Answer: This question evaluates stateful transaction processing and aggregation skills, including per-(account,currency) balance maintenance, timestamp-based ordering, rejection rules, and overdraft coverage via a platform account.
Part 1: Final Balances by Account and Currency
Constraints
- 0 <= len(transactions) <= 10^5
- timestamp fits in 64-bit signed integer
- amount fits in 64-bit signed integer
- Each transaction string is well-formed
Examples
Input: (['alice,10,USD,100','bob,12,EUR,40','alice,11,USD,-30','alice,15,EUR,50'],)
Expected Output: ['alice,EUR,50','alice,USD,70','bob,EUR,40']
Explanation: USD and EUR are tracked separately for alice.
Input: (['a,1,USD,10','a,2,USD,-10','b,3,USD,5'],)
Expected Output: ['b,USD,5']
Explanation: Account a ends at 0 USD and is excluded.
Input: ([],)
Expected Output: []
Explanation: Edge case: no transactions means no balances.
Input: (['a,1,USD,-7'],)
Expected Output: ['a,USD,-7']
Explanation: Negative final balances are allowed in Part 1.
Solution
def solution(transactions):
balances = {}
for record in transactions:
account_id, _timestamp, currency, amount = record.split(',')
key = (account_id, currency)
balances[key] = balances.get(key, 0) + int(amount)
rows = []
for (account_id, currency), balance in balances.items():
if balance != 0:
rows.append((account_id, currency, balance))
rows.sort()
return [f'{account_id},{currency},{balance}' for account_id, currency, balance in rows]Time complexity: O(n + k log k), where n is the number of transactions and k is the number of distinct (account_id, currency) pairs. Space complexity: O(k).
Hints
- Use a hash map keyed by (account_id, currency).
- Aggregate first, then filter out zero balances and sort once at the end.
Part 2: Final Balances with Rejected Overdrafts
Constraints
- 0 <= len(transactions) <= 10^5
- timestamp fits in 64-bit signed integer
- amount fits in 64-bit signed integer
- Each transaction string is well-formed
Examples
Input: (['alice,5,USD,-30','alice,1,USD,100','alice,3,USD,-80','bob,2,EUR,20','bob,4,EUR,-25'],)
Expected Output: ['alice,USD,20','bob,EUR,20']
Explanation: After sorting by time, bob's debit and alice's last debit are both rejected.
Input: (['a,2,USD,-5','a,2,USD,10'],)
Expected Output: ['a,USD,10']
Explanation: Same timestamp uses input order, so the debit is seen first and rejected.
Input: (['a,1,USD,10','a,2,USD,-10'],)
Expected Output: []
Explanation: The final balance is exactly 0, so it is omitted.
Input: ([],)
Expected Output: []
Explanation: Edge case: empty input.
Solution
def solution(transactions):
ordered = []
for index, record in enumerate(transactions):
account_id, timestamp, currency, amount = record.split(',')
ordered.append((int(timestamp), index, account_id, currency, int(amount)))
ordered.sort()
balances = {}
for _timestamp, _index, account_id, currency, amount in ordered:
key = (account_id, currency)
current = balances.get(key, 0)
if amount < 0 and current + amount < 0:
continue
new_balance = current + amount
if new_balance == 0:
balances.pop(key, None)
else:
balances[key] = new_balance
rows = [(account_id, currency, balance) for (account_id, currency), balance in balances.items()]
rows.sort()
return [f'{account_id},{currency},{balance}' for account_id, currency, balance in rows]Time complexity: O(n log n + k log k), where n is the number of transactions and k is the number of distinct (account_id, currency) pairs. Space complexity: O(n + k).
Hints
- Sort by timestamp, but keep the original index so ties follow input order.
- Before applying a debit, check whether current_balance + amount would be negative.
Part 3: Final Balances with Platform Shortfall Coverage
Constraints
- 0 <= len(transactions) <= 10^5
- timestamp fits in 64-bit signed integer
- amount fits in 64-bit signed integer
- platform_account_id is a string identifier
- Each transaction string is well-formed
Examples
Input: (['platform,1,USD,100','alice,2,USD,30','alice,3,USD,-50'],'platform')
Expected Output: ['platform,USD,80']
Explanation: Alice uses her 30 first, and the platform covers the remaining 20.
Input: (['platform,1,USD,10','alice,2,USD,5','alice,3,USD,-20'],'platform')
Expected Output: ['alice,USD,5','platform,USD,10']
Explanation: The shortfall is 15, but the platform only has 10 USD, so the debit is rejected.
Input: (['platform,1,EUR,50','alice,2,USD,10','alice,3,USD,-15'],'platform')
Expected Output: ['alice,USD,10','platform,EUR,50']
Explanation: Platform funds must be in the same currency, so EUR cannot cover a USD shortfall.
Input: (['platform,1,USD,10','platform,2,USD,-15','alice,3,USD,2','alice,4,USD,-1'],'platform')
Expected Output: ['alice,USD,1','platform,USD,10']
Explanation: A debit on the platform account itself is rejected if it would make the platform balance negative.
Input: ([],'platform')
Expected Output: []
Explanation: Edge case: no transactions.
Solution
def solution(transactions, platform_account_id):
ordered = []
for index, record in enumerate(transactions):
account_id, timestamp, currency, amount = record.split(',')
ordered.append((int(timestamp), index, account_id, currency, int(amount)))
ordered.sort()
balances = {}
def set_balance(key, value):
if value == 0:
balances.pop(key, None)
else:
balances[key] = value
for _timestamp, _index, account_id, currency, amount in ordered:
key = (account_id, currency)
current = balances.get(key, 0)
if amount >= 0:
set_balance(key, current + amount)
continue
debit = -amount
if current >= debit:
set_balance(key, current - debit)
continue
if account_id == platform_account_id:
continue
shortfall = debit - current
platform_key = (platform_account_id, currency)
platform_balance = balances.get(platform_key, 0)
if platform_balance < shortfall:
continue
set_balance(key, 0)
set_balance(platform_key, platform_balance - shortfall)
rows = [(account_id, currency, balance) for (account_id, currency), balance in balances.items()]
rows.sort()
return [f'{account_id},{currency},{balance}' for account_id, currency, balance in rows]Time complexity: O(n log n + k log k), where n is the number of transactions and k is the number of distinct (account_id, currency) pairs. Space complexity: O(n + k).
Hints
- For an insufficient debit, compute shortfall = debit_amount - account_balance.
- Only change balances after confirming the platform has enough in the same currency.