当弹跳次数 b 高达 10¹² 时,朴素模拟会超时;本文介绍通过检测状态序列的「起始段(prefix)」与「循环节(cycle)」,将时间复杂度从 o(b) 降至 o(n),完美应对大规模查询。
当弹跳次数 b 高达 10¹² 时,朴素模拟会超时;本文介绍通过检测状态序列的「起始段(prefix)」与「循环节(cycle)」,将时间复杂度从 o(b) 降至 o(n),完美应对大规模查询。
在 Hackerrank 的 Movement Slots 问题中,一个球从初始槽位 a 出发,在由 n 个槽位组成的环形轮盘上按规则弹跳 b 次:若当前位于槽位 i,则下一次落点为 (i + jumps[i]) % n(负数自动模运算支持逆时针)。朴素实现使用 for _ in range(b) 循环模拟,时间复杂度为 O(b),面对 b ≤ 10¹² 的极端输入必然超时。
根本优化思路是:状态空间有限(仅 n 个不同槽位),因此球的位置序列必在有限步内进入循环。该序列为典型的「ρ 形序列」(rho-shaped sequence):先经过一段非重复的起始路径(prefix),长度记为 X;随后进入长度为 Y 的纯循环(cycle),即存在最小正整数 Y,使得对所有 k ≥ X,有 pos[k + Y] == pos[k]。
由此可将计算拆解为三步:
- 检测循环结构:使用 Floyd 判圈法(快慢指针)或哈希表记录首次访问位置,获取 X(入环前步数)和 Y(环长);
-
分类计算:
- 若 b ≤ X:直接模拟 b 步;
- 若 b > X:只需模拟 X + (b − X) % Y 步(跳过完整循环次数);
- 批量查询优化:对每个查询 (a, b) 独立执行上述逻辑;若 n 较小而 q 较大,可预处理每个起点 a 对应的 X_a, Y_a, cycle_a 数组,实现均摊 O(1) 查询。
以下是完整、健壮的 Python 实现(含循环检测与边界处理):
def detect_cycle_start_and_length(jumps, start):
n = len(jumps)
# 使用字典记录每个位置首次出现的步数
visited = {}
current = start
step = 0
while current not in visited:
visited[current] = step
current = (current + jumps[current]) % n
step += 1
X = visited[current] # 入环点步数(起始段长度)
Y = step - X # 循环节长度
return X, Y, visited
def final_slot_optimized(a, b, jumps):
if b == 0:
return a
n = len(jumps)
X, Y, visited = detect_cycle_start_and_length(jumps, a)
if b <= X:
# 未入环,直接模拟
pos = a
for _ in range(b):
pos = (pos + jumps[pos]) % n
return pos
else:
# 跳过完整循环:只模拟 X + 剩余偏移
remaining = (b - X) % Y
pos = a
# 模拟前 X 步到达环入口
for _ in range(X):
pos = (pos + jumps[pos]) % n
# 再模拟 remaining 步(在环内)
for _ in range(remaining):
pos = (pos + jumps[pos]) % n
return pos
# 批量查询主函数(适配原题输入格式)
def find_last_bounce(jumps, queries):
results = []
for a, b in queries:
results.append(final_slot_optimized(a, b, jumps))
return results✅ 关键优势:
- 单次查询最坏时间复杂度为 O(n)(循环检测 + 最多 X + Y ≤ 2n 步模拟),与 b 无关;
- 空间复杂度 O(n),用于存储访问记录;
- 完全兼容负向跳跃(Python 的 % 对负数自动归一化到 [0, n−1]);
- 无需额外图论建模,逻辑清晰、易于调试。
⚠️ 注意事项:
- jumps[i] 可为负数,务必使用 (i + jumps[i]) % n 而非 (i + jumps[i]) % n 的错误变体(如 + n) % n 更安全,但 Python % 已保证结果非负);
- 若 n = 0 需提前校验(题目保证 n ≥ 1);
- Floyd 法可将空间降至 O(1),但哈希表法更直观且 n 通常 ≤ 10⁵,内存友好。
总结:面对隐式状态图上的超长路径查询,识别并利用「有限状态 ⇒ 必然循环」这一本质特性,是解决此类问题的通用范式。本方案不仅通过循环压缩将 b = 10¹² 降维至 ≤ 2×10⁵ 步内完成,更为同类问题(如链表环检测、模幂周期、状态机长期演化)提供了可迁移的方法论。










