题目重点:把多阶段匹配写成确定的优先级,避免同金额多张发票、日期并列与宽容范围的歧义。
题意与假设
这道练习题保留了面经中值得训练的核心约束,并将需要澄清的条件单独列出。现场拿到变体时,先和面试官确认这些条件。
- 训练版一次匹配一笔 payment,invoice 的 ID 唯一;金额统一币种且使用整数最小单位。
- 先有 ID 命中即返回,即使金额不一致也按第 1 段规则返回;若实际题面要求 ID+金额同时满足,应替换该判定。
- 无 ID 命中时,同金额候选选日期最早;再无候选时,满足 |invoice.amount−payment.amount|≤forgiveness 的候选选日期最早;同日用原始输入顺序稳定破平局。
解题路线
- 先明确三个优先层级和每层的比较器,写成能独立测试的 matchById、matchExactAmount、matchWithinTolerance。上一层有结果立即返回,不能让日期更早的低优先级候选盖掉 ID。
- 在单笔情况下,ID 可用哈希索引 O(n) 预建;金额候选若列表未排序,线性扫描并维护最优 (date,index),无需先全量排序。
- 金额边界用整数和绝对差,forgiveness<0 视为无效参数;币种不同先禁止比较,除非题面提供明确 FX 规则。
- 批量支付若同一 invoice 只能用一次,需要额外 used 集与原子占用;若多笔可共享则不需。此约束题目未公开,不能擅自套到账务对账。
手动走一遍
训练例:payment id=missing、amount=1000,候选 A(1000, 2025-01-03)、B(1000, 2025-01-01)、C(995, 2024-12-01)。即使 forgiveness=10,也选 B,因为 exact amount 层优先;若 payment 指向 ID=A,则选 A。这个例子检验优先级而非宣称题目样例。
规模与取舍
单笔、n 张发票最多三次线性扫描 O(n) 时间、O(1) 额外空间;多笔可建 ID/金额索引减少常见查询,但维护最早日期与重复占用状态要更复杂。
易错点与追问
容易失分的边界
- 把全部候选按日期排序后取首张,破坏 ID 和 exact amount 的优先级。
- 货币转 float 后做等值或宽容判断。
- 没有约定同日期平局、ID 重复、不同币种和单发票复用。
- 把第三段转述当作有完整官方规格。
继续深挖
- ID 命中但金额不同,应返回、报错还是落到下一层?
- 批量 payment 与 invoice 一对一全局匹配时,局部最早日期还最优吗?
- 发票日期有时区与精度差异,稳定排序键如何定义?
