登录
OAmaster

Implement a simplified version of a banking system. All operations have a timestamp parameter (stringified ms, range 1 to 10⁹, strictly increasing).

Level 1 — Account Creation, Deposit, Transfer

  • create_account(timestamp, account_id) -> bool
  • deposit(timestamp, account_id, amount) -> int | None
  • transfer(timestamp, source_account_id, target_account_id, amount) -> int | None

Level 2 — Top Spenders

  • top_spenders(timestamp, n) -> list[str] — top n accounts by highest outgoing total (transfer source + pay). Format "account_id(total_outgoing)". Tiebreak alphabetically.

Level 3 — Scheduled Payments with 2% Cashback

  • pay(timestamp, account_id, amount) -> str | None — withdraw amount. Provides 2% cashback (rounded down) refunded 24 hours later (86_400_000 ms). Returns "payment{ordinal}". Cashback processes before any other transaction at its refund timestamp.
  • get_payment_status(timestamp, account_id, payment_id) -> str | None"IN_PROGRESS" or "CASHBACK_RECEIVED".

Level 4 — Account Merging with Balance History

  • merge_accounts(timestamp, account_id_1, account_id_2) -> bool — merge account_id_2 into account_id_1. Pending cashbacks for account 2 still refund to account 1. Queries with old payment_id must still work via account 1.
  • get_balance(timestamp, account_id, time_at) -> int | None — historical balance after operations at time_at.

解法

四级状态机。账户字典维护 balance + outgoing + history。Cashback 用按 (refund_ts, payment_id) 排序的小顶堆,每次操作开头先 flush,确保同一时间戳的退款先于用户操作发生。合并用 merged_to 重定向表(并查集风格)让旧账户 id 和 payment id 仍能解析。get_balance 在账户历史列表上二分。多数操作均摊 O(log n)。设计题,只给 Python 实现。

import heapq, bisect
from collections import defaultdict
from typing import Optional, List

class Bank:
    DAY_MS = 86_400_000

    def __init__(self):
        self.accounts = {}
        self.payments = {}
        self.payment_counter = 0
        self.cashback_heap = []
        self.merged_to = {}

    def _resolve(self, aid):
        while aid in self.merged_to: aid = self.merged_to[aid]
        return aid

    def _flush_cashback(self, ts):
        while self.cashback_heap and self.cashback_heap[0][0] <= ts:
            refund_ts, pid = heapq.heappop(self.cashback_heap)
            pay = self.payments[pid]
            if pay["status"] == "CASHBACK_RECEIVED": continue
            aid = self._resolve(pay["account"])
            if aid not in self.accounts: continue
            refund = pay["amount"] // 50
            acc = self.accounts[aid]
            acc["balance"] += refund
            acc["history"].append((refund_ts, acc["balance"]))
            pay["status"] = "CASHBACK_RECEIVED"

    def create_account(self, ts, aid):
        self._flush_cashback(ts)
        if aid in self.accounts: return False
        self.accounts[aid] = {"balance": 0, "outgoing": 0, "history": [(ts, 0)]}
        return True

    def deposit(self, ts, aid, amount):
        self._flush_cashback(ts)
        aid = self._resolve(aid)
        if aid not in self.accounts: return None
        acc = self.accounts[aid]
        acc["balance"] += amount
        acc["history"].append((ts, acc["balance"]))
        return acc["balance"]

    def transfer(self, ts, src, dst, amount):
        self._flush_cashback(ts)
        src = self._resolve(src); dst = self._resolve(dst)
        if src == dst or src not in self.accounts or dst not in self.accounts: return None
        sa = self.accounts[src]
        if sa["balance"] < amount: return None
        sa["balance"] -= amount; sa["outgoing"] += amount
        self.accounts[dst]["balance"] += amount
        sa["history"].append((ts, sa["balance"]))
        self.accounts[dst]["history"].append((ts, self.accounts[dst]["balance"]))
        return sa["balance"]

    def top_spenders(self, ts, n):
        self._flush_cashback(ts)
        items = sorted(
            ((aid, acc["outgoing"]) for aid, acc in self.accounts.items()),
            key=lambda x: (-x[1], x[0])
        )
        return [f"{aid}({amt})" for aid, amt in items[:n]]

    def pay(self, ts, aid, amount):
        self._flush_cashback(ts)
        aid = self._resolve(aid)
        if aid not in self.accounts: return None
        acc = self.accounts[aid]
        if acc["balance"] < amount: return None
        acc["balance"] -= amount; acc["outgoing"] += amount
        acc["history"].append((ts, acc["balance"]))
        self.payment_counter += 1
        pid = f"payment{self.payment_counter}"
        cashback_ts = ts + self.DAY_MS
        self.payments[pid] = {"account": aid, "amount": amount, "cashback_ts": cashback_ts, "status": "IN_PROGRESS"}
        heapq.heappush(self.cashback_heap, (cashback_ts, pid))
        return pid

    def get_payment_status(self, ts, aid, pid):
        self._flush_cashback(ts)
        aid = self._resolve(aid)
        if aid not in self.accounts or pid not in self.payments: return None
        if self._resolve(self.payments[pid]["account"]) != aid: return None
        return self.payments[pid]["status"]

    def merge_accounts(self, ts, a1, a2):
        self._flush_cashback(ts)
        a1 = self._resolve(a1); a2 = self._resolve(a2)
        if a1 == a2 or a1 not in self.accounts or a2 not in self.accounts: return False
        acc1 = self.accounts[a1]; acc2 = self.accounts[a2]
        acc1["balance"] += acc2["balance"]
        acc1["outgoing"] += acc2["outgoing"]
        merged_hist = sorted(acc1["history"] + acc2["history"], key=lambda x: x[0])
        clean = []
        for tts, bal in merged_hist:
            if clean and clean[-1][0] == tts:
                clean[-1] = (tts, bal)
            else:
                clean.append((tts, bal))
        acc1["history"] = clean
        acc1["history"].append((ts, acc1["balance"]))
        self.merged_to[a2] = a1
        del self.accounts[a2]
        return True

    def get_balance(self, ts, aid, time_at):
        self._flush_cashback(ts)
        aid = self._resolve(aid)
        if aid not in self.accounts: return None
        hist = self.accounts[aid]["history"]
        keys = [h[0] for h in hist]
        idx = bisect.bisect_right(keys, time_at) - 1
        if idx < 0: return None
        return hist[idx][1]

You've been asked to program a bot for a popular bank. Every request has a timestamp in seconds (strictly increasing).

  • "deposit <timestamp> <holder_id> <amount>"
  • "withdraw <timestamp> <holder_id> <amount>" — withdraw + 2% cashback rounded down, refunded 24h (86400 sec) later. If the same timestamp triggers both a withdraw and a cashback, cashback happens first.

Invalid requests: invalid account number, or insufficient funds. Return final balances after last request, or [-request_id] (1-based) on first invalid.

解法

对请求单次扫描,待退款用按 refund_ts 排序的小顶堆。每条请求先 flush 所有 refund_ts ≤ ts 的退款再处理。首次非法立即返回 [-i](1 起)。复杂度 O((R + C) log C)C 为退款数。

import heapq

def solution(balances, requests):
    n_acc = len(balances)
    bal = list(balances)
    cashback = []

    def flush(ts):
        while cashback and cashback[0][0] <= ts:
            rt, idx, amt = heapq.heappop(cashback)
            bal[idx] += amt

    for i, req in enumerate(requests, 1):
        parts = req.split()
        op, ts, hid, amt = parts[0], int(parts[1]), int(parts[2]), int(parts[3])
        flush(ts)
        if hid < 1 or hid > n_acc:
            return [-i]
        idx = hid - 1
        if op == "deposit":
            bal[idx] += amt
        elif op == "withdraw":
            if bal[idx] < amt: return [-i]
            bal[idx] -= amt
            cashback_amt = (amt * 2) // 100
            heapq.heappush(cashback, (ts + 86400, idx, cashback_amt))
        else:
            return [-i]
    return bal

Same 4-level structure as Problem 1, signatures use Optional<Integer> / Optional<String>.

Level 1

  • createAccount(ts, accountId) -> boolean
  • Optional<Integer> deposit(ts, accountId, amount)
  • Optional<Integer> transferMoney(ts, sourceAccountId, targetAccountId, amount)

Level 2 — Ranking

  • topSpenders(ts, n) — top n accounts by total outgoing. Format "accountId(total)".

Level 3 — Scheduled Payments

  • schedulePayment(ts, accountId, amount, delay) — schedule a payment that executes at ts + delay. Returns paymentN id.
  • cancelScheduledPayment(ts, paymentId) — only if still SCHEDULED.

Level 4 — Merge Accounts

  • boolean mergeAccounts(ts, accountId1, accountId2)
  • Optional<Integer> getBalance(ts, accountId, timeAt)

解法

与第 1 题同样的 4 级骨架,Level 3 把 cashback 换成显式的 schedulePayment / cancelScheduledPayment API;待执行付款用同样的小顶堆。状态机:SCHEDULED → EXECUTED / FAILED / CANCELLED。设计题,只给 Python 骨架。

import heapq

class Bank2:
    def __init__(self):
        self.accounts = {}
        self.scheduled = {}
        self.pay_counter = 0
        self.queue = []
        self.merged = {}

    def _resolve(self, aid):
        while aid in self.merged: aid = self.merged[aid]
        return aid

    def _flush_scheduled(self, ts):
        while self.queue and self.queue[0][0] <= ts:
            et, pid = heapq.heappop(self.queue)
            p = self.scheduled[pid]
            if p["status"] != "SCHEDULED": continue
            aid = self._resolve(p["account"])
            if aid not in self.accounts:
                p["status"] = "CANCELLED"; continue
            acc = self.accounts[aid]
            if acc["balance"] < p["amount"]:
                p["status"] = "FAILED"; continue
            acc["balance"] -= p["amount"]
            acc["outgoing"] += p["amount"]
            acc["history"].append((et, acc["balance"]))
            p["status"] = "EXECUTED"

    def schedulePayment(self, ts, aid, amt, delay):
        self._flush_scheduled(ts)
        aid = self._resolve(aid)
        if aid not in self.accounts: return None
        self.pay_counter += 1
        pid = f"payment{self.pay_counter}"
        exec_ts = ts + delay
        self.scheduled[pid] = {"account": aid, "amount": amt, "exec_ts": exec_ts, "status": "SCHEDULED"}
        heapq.heappush(self.queue, (exec_ts, pid))
        return pid

    def cancelScheduledPayment(self, ts, pid):
        self._flush_scheduled(ts)
        if pid not in self.scheduled: return False
        if self.scheduled[pid]["status"] != "SCHEDULED": return False
        self.scheduled[pid]["status"] = "CANCELLED"
        return True
Pro 会员

解锁剩余 1 道 OA 真题

开通后立即解锁完整题面 + Python / Java / C++ 三语题解。 全站 165 家公司 · 1000+ 道 OA · 月度更新。