SWE Intern / New Grad · HackerRank OA

Stripe 商户交易 Fraud Score:逐笔规则、频次与小时奖惩

交易与规则逐行对齐,按金额乘法、累计客户频次和同小时频次顺序更新每个商户的整数分数。

题目重点:交易与规则逐行对齐,按金额乘法、累计客户频次和同小时频次顺序更新每个商户的整数分数。

题意与假设

这道练习题保留了面经中值得训练的核心约束,并将需要澄清的条件单独列出。现场拿到变体时,先和面试官确认这些条件。

  • 每笔 transaction 与同下标 rule 一一对应;transaction=(merchant,amount,customer,hour),rule=(minimum_amount,multiplier,repeat_addition,hourly_adjustment)。
  • 只接受初始商户表中已有的 merchant,按表中顺序输出;CSV 简化成无引号的四段/两段格式。正式题如支持字段内逗号须换 parser。
  • 高额条件严格 amount>minimum_amount;同商户客户第 4 笔起加分;同商户客户同小时第 3 笔起作时段奖惩。当前笔先计入次数再应用三规则。

解题路线

  1. 解析并检查交易/规则行数、商户 ID 唯一、小时 0..23;把每个商户分数保存在 map,输出顺序另用列表保持。
  2. 按输入顺序处理。对 (merchant,customer) 与 (merchant,customer,hour) 分别递增计数,绝不能把两个客户或不同商户的频次混在一起。
  3. 取商户当前分数,若 amount>limit 先乘 factor;若 pair count>3 再加 addition;若 hourly count≥3,12..17 加 adjustment,9..11 或 18..22 减 adjustment,其他小时不动;保存新分数。
  4. 用扫描既有历史的慢参考实现独立对照计数 map,覆盖第 2/3/4 笔、时段边界、金额恰等阈值、规则顺序和负分数。

手动走一遍

训练例初始 m=10,同一客户同一商户同一 12 点四笔金额均不超阈值、每笔 addition=2、adjustment=3:前两笔保持 10;第 3 笔小时规则加 3 得 13;第 4 笔客户总频次加 2 且小时规则加 3 得 18。若第 4 笔也触发乘 2,应先将 13 乘 2 再加 2+3,结果 31。

Python 参考实现

下面的代码按上文假设实现,适合本地运行并追加自己的测试用例。

代码区可横向滚动;使用键盘时,先聚焦代码区再按方向键查看。

from __future__ import annotations


def score_merchants(
    transactions: list[str], rules: list[str], merchants: list[str]
) -> list[str]:
    """Training contract from the reported OA; amounts/scores are integers.

    Transaction: merchant,amount,customer,hour
    Rule: minimum_amount,multiplier,repeat_addition,hourly_adjustment
    Merchant: id,initial_score
    """
    if len(transactions) != len(rules):
        raise ValueError("each transaction requires its own rule")
    scores: dict[str, int] = {}
    order: list[str] = []
    for line in merchants:
        name, raw_score = (field.strip() for field in line.split(","))
        if not name or name in scores:
            raise ValueError("merchant IDs must be nonempty and unique")
        scores[name] = int(raw_score)
        order.append(name)

    pair_counts: dict[tuple[str, str], int] = {}
    hourly_counts: dict[tuple[str, str, int], int] = {}
    for transaction, rule in zip(transactions, rules):
        merchant, raw_amount, customer, raw_hour = (
            field.strip() for field in transaction.split(",")
        )
        raw_limit, raw_factor, raw_add, raw_adjust = (
            field.strip() for field in rule.split(",")
        )
        amount, hour = int(raw_amount), int(raw_hour)
        limit, factor, addition, adjustment = map(
            int, (raw_limit, raw_factor, raw_add, raw_adjust)
        )
        if merchant not in scores or not customer or not 0 <= hour <= 23:
            raise ValueError("unknown merchant, empty customer or invalid hour")

        pair = (merchant, customer)
        hourly = (merchant, customer, hour)
        pair_counts[pair] = pair_counts.get(pair, 0) + 1
        hourly_counts[hourly] = hourly_counts.get(hourly, 0) + 1

        score = scores[merchant]
        if amount > limit:
            score *= factor
        if pair_counts[pair] > 3:
            score += addition
        if hourly_counts[hourly] >= 3:
            if 12 <= hour <= 17:
                score += adjustment
            elif 9 <= hour <= 11 or 18 <= hour <= 22:
                score -= adjustment
        scores[merchant] = score
    return [f"{merchant},{scores[merchant]}" for merchant in order]

复杂度

T 笔交易、M 个商户时,哈希 map 期望 O(T+M) 时间、O(T+M) 最坏不同 pair/hour 键空间;朴素历史重扫为 O(T²),仅用于小样本独立验证。超大整数乘法需考虑语言的溢出或业务上限。

易错点与追问

容易失分的边界

  • 把规则列表当通用规则集而不是逐笔对齐。
  • 用 >3 判断小时频次,错过第 3 笔;用 ≥3 判断总频次,提前一笔加分。
  • 先加分再乘使结果不同。
  • hour 9/11/12/17/18/22 的包含边界写错,或不稳定输出商户顺序。
  • 把作者的 AI 整理稿视作官方精确签名,忽视是否存在引号和负数约束缺口。

继续深挖

  • 交易同一小时但日期不同是否仍算同一桶?题目只有 hour 字段,不能自行推断跨天。
  • 规则若按商户动态更新而非逐笔一一对应,数据模型怎样改?
  • 若要并行处理同商户交易,怎样保持原始顺序与最终分数一致?
✓已复制微信号!