题目重点:Salesforce Futureforce OA 描述每秒同时替换所有 01;本文推导零数量与阻塞时间递推,解释为什么不能把同步过程当顺序交换。
题意与假设
这道练习题保留了面经中值得训练的核心约束,并将需要澄清的条件单独列出。现场拿到变体时,先和面试官确认这些条件。
- 每一秒先在旧串上找到全部 01,再同时交换这些互不重叠的相邻对;不能在同一秒把新产生的 01 再处理。
- 输入只含字符 0/1;空串、全 0、全 1 或已是若干 1 后接若干 0 时答案为 0。代码对其他字符抛错,这是训练版接口约定。
- 只求稳定时间,不返回交换路径;不存在长度上限要求,因而优先用 O(n) 扫描。
解题路线
- 左到右扫描,zeros_seen 记录已看到的 0。若当前 1 前面没有 0,它无需移动;否则它最终要越过这些 0,至少需要 zeros_seen 秒。
- 把此前所有 1 处理完所需的时间记为 seconds。当前 1 不能和前一个需移动的 1 在同一时刻越过同一个 0,存在一秒的流水线阻塞,因此新的完成时间是 max(seconds+1, zeros_seen)。
- 不变量:扫描前缀后 seconds 是该前缀独立同步交换直到没有 01 的总秒数;新 0 不改变此前 1 的完成时间,新 1 的完成时间受其前方 0 数与前方移动的 1 完成时刻共同约束。扫描结束即全串答案。
手动走一遍
010110:遇第一个需移动的 1 时 zeros_seen=1、seconds=1;第二个 1 前有两个 0,seconds=max(2,2)=2;第三个 1 前仍有两个 0,seconds=max(3,2)=3。逐秒同步模拟为 010110→101010→110100→111000,共 3 秒。对于 001,单个 1 要越过两个 0,答案 2。
Python 参考实现
下面的代码按上文假设实现,适合本地运行并追加自己的测试用例。
代码区可横向滚动;使用键盘时,先聚焦代码区再按方向键查看。
def seconds_to_remove_01(bits: str) -> int:
"""Simultaneously replace every 01 with 10 until no 01 remains."""
if any(char not in "01" for char in bits):
raise ValueError("input must be a binary string")
zeros_seen = 0
seconds = 0
for char in bits:
if char == "0":
zeros_seen += 1
elif zeros_seen:
seconds = max(seconds + 1, zeros_seen)
return seconds
复杂度
n 为字符串长度。扫描一次,时间 O(n),辅助空间 O(1)。若逐秒构造新串,最坏可能需要 O(n²) 时间;而且原地从左往右修改容易错误地在同一秒处理新产生的 01。
易错点与追问
容易失分的边界
- 把 01 顺序替换,导致一次循环里让同一个 1 连续跨多个 0,少算秒数。
- 只统计 0 的总量:多个 1 可能互相阻塞,010110 的答案 3,大于前面 0 的最大数 2。
- 无条件在每个 1 上 seconds+1;若其前面没有 0,已排序前缀本来不需要时间。
- 把 10 当成待替换对,方向反了;终态应为所有 1 在所有 0 之前。
继续深挖
- 若要输出每秒的字符串,需要真正同步模拟,并接受输出本身可达 O(n²) 大小。
- 若规则换为 10→01,反向扫描或交换 0/1 的角色再推递推,不能照搬本题变量含义。
- 若允许一次交换任意相邻 01 而非全部同时交换,最少操作数等于逆序对数,时间概念与本题不同。
