Off-by-one on range boundaries
Wrong move: Loop endpoints miss first/last candidate.
Usually fails on: Fails on minimal arrays and exact-boundary answers.
Fix: Re-derive loops from inclusive/exclusive ranges before coding.
Move from brute-force thinking to an efficient approach using array strategy.
A certain bug's home is on the x-axis at position x. Help them get there from position 0.
The bug jumps according to the following rules:
a positions forward (to the right).b positions backward (to the left).forbidden positions.The bug may jump forward beyond its home, but it cannot jump to positions numbered with negative integers.
Given an array of integers forbidden, where forbidden[i] means that the bug cannot jump to the position forbidden[i], and integers a, b, and x, return the minimum number of jumps needed for the bug to reach its home. If there is no possible sequence of jumps that lands the bug on position x, return -1.
Example 1:
Input: forbidden = [14,4,18,1,15], a = 3, b = 15, x = 9 Output: 3 Explanation: 3 jumps forward (0 -> 3 -> 6 -> 9) will get the bug home.
Example 2:
Input: forbidden = [8,3,16,6,12,20], a = 15, b = 13, x = 11 Output: -1
Example 3:
Input: forbidden = [1,6,2,14,5,17,4], a = 16, b = 9, x = 7 Output: 2 Explanation: One jump forward (0 -> 16) then one jump backward (16 -> 7) will get the bug home.
Constraints:
1 <= forbidden.length <= 10001 <= a, b, forbidden[i] <= 20000 <= x <= 2000forbidden are distinct.x is not forbidden.Problem summary: A certain bug's home is on the x-axis at position x. Help them get there from position 0. The bug jumps according to the following rules: It can jump exactly a positions forward (to the right). It can jump exactly b positions backward (to the left). It cannot jump backward twice in a row. It cannot jump to any forbidden positions. The bug may jump forward beyond its home, but it cannot jump to positions numbered with negative integers. Given an array of integers forbidden, where forbidden[i] means that the bug cannot jump to the position forbidden[i], and integers a, b, and x, return the minimum number of jumps needed for the bug to reach its home. If there is no possible sequence of jumps that lands the bug on position x, return -1.
Start with the most direct exhaustive search. That gives a correctness anchor before optimizing.
Pattern signal: Array · Hash Map
[14,4,18,1,15] 3 15 9
[8,3,16,6,12,20] 15 13 11
[1,6,2,14,5,17,4] 16 9 7
reachable-nodes-with-restrictions)maximum-number-of-jumps-to-reach-the-last-index)Source-backed implementations are provided below for direct study and interview prep.
// Accepted solution for LeetCode #1654: Minimum Jumps to Reach Home
class Solution {
public int minimumJumps(int[] forbidden, int a, int b, int x) {
Set<Integer> s = new HashSet<>();
for (int v : forbidden) {
s.add(v);
}
Deque<int[]> q = new ArrayDeque<>();
q.offer(new int[] {0, 1});
final int n = 6000;
boolean[][] vis = new boolean[n][2];
vis[0][1] = true;
for (int ans = 0; !q.isEmpty(); ++ans) {
for (int t = q.size(); t > 0; --t) {
var p = q.poll();
int i = p[0], k = p[1];
if (i == x) {
return ans;
}
List<int[]> nxt = new ArrayList<>();
nxt.add(new int[] {i + a, 1});
if ((k & 1) == 1) {
nxt.add(new int[] {i - b, 0});
}
for (var e : nxt) {
int j = e[0];
k = e[1];
if (j >= 0 && j < n && !s.contains(j) && !vis[j][k]) {
q.offer(new int[] {j, k});
vis[j][k] = true;
}
}
}
}
return -1;
}
}
// Accepted solution for LeetCode #1654: Minimum Jumps to Reach Home
func minimumJumps(forbidden []int, a int, b int, x int) (ans int) {
s := map[int]bool{}
for _, v := range forbidden {
s[v] = true
}
q := [][2]int{[2]int{0, 1}}
const n = 6000
vis := make([][2]bool, n)
vis[0][1] = true
for ; len(q) > 0; ans++ {
for t := len(q); t > 0; t-- {
p := q[0]
q = q[1:]
i, k := p[0], p[1]
if i == x {
return
}
nxt := [][2]int{[2]int{i + a, 1}}
if k&1 == 1 {
nxt = append(nxt, [2]int{i - b, 0})
}
for _, e := range nxt {
j, l := e[0], e[1]
if j >= 0 && j < n && !s[j] && !vis[j][l] {
q = append(q, [2]int{j, l})
vis[j][l] = true
}
}
}
}
return -1
}
# Accepted solution for LeetCode #1654: Minimum Jumps to Reach Home
class Solution:
def minimumJumps(self, forbidden: List[int], a: int, b: int, x: int) -> int:
s = set(forbidden)
q = deque([(0, 1)])
vis = {(0, 1)}
ans = 0
while q:
for _ in range(len(q)):
i, k = q.popleft()
if i == x:
return ans
nxt = [(i + a, 1)]
if k & 1:
nxt.append((i - b, 0))
for j, k in nxt:
if 0 <= j < 6000 and j not in s and (j, k) not in vis:
q.append((j, k))
vis.add((j, k))
ans += 1
return -1
// Accepted solution for LeetCode #1654: Minimum Jumps to Reach Home
struct Solution;
use std::collections::HashSet;
use std::collections::VecDeque;
use std::iter::FromIterator;
impl Solution {
fn minimum_jumps(forbidden: Vec<i32>, a: i32, b: i32, x: i32) -> i32 {
let mut visited: HashSet<(i32, bool)> = HashSet::new();
let mut queue: VecDeque<(i32, bool, i32)> = VecDeque::new();
let max_pos = forbidden.iter().max().unwrap_or(&0) + a + b + x;
let forbidden: HashSet<i32> = HashSet::from_iter(forbidden);
visited.insert((0, false));
queue.push_back((0, false, 0));
while let Some((pos, backjump, step)) = queue.pop_front() {
if pos == x {
return step;
}
let next_pos = pos + a;
if next_pos <= max_pos
&& !forbidden.contains(&(next_pos))
&& visited.insert((next_pos, false))
{
queue.push_back((next_pos, false, step + 1));
}
if !backjump {
let next_pos = pos - b;
if next_pos >= 0
&& !forbidden.contains(&(next_pos))
&& visited.insert((next_pos, true))
{
queue.push_back((next_pos, true, step + 1));
}
}
}
-1
}
}
#[test]
fn test() {
let forbidden = vec![14, 4, 18, 1, 15];
let a = 3;
let b = 15;
let x = 9;
let res = 3;
assert_eq!(Solution::minimum_jumps(forbidden, a, b, x), res);
let forbidden = vec![8, 3, 16, 6, 12, 20];
let a = 15;
let b = 13;
let x = 11;
let res = -1;
assert_eq!(Solution::minimum_jumps(forbidden, a, b, x), res);
let forbidden = vec![1, 6, 2, 14, 5, 17, 4];
let a = 16;
let b = 9;
let x = 7;
let res = 2;
assert_eq!(Solution::minimum_jumps(forbidden, a, b, x), res);
}
// Accepted solution for LeetCode #1654: Minimum Jumps to Reach Home
function minimumJumps(forbidden: number[], a: number, b: number, x: number): number {
const s: Set<number> = new Set(forbidden);
const q: [number, number][] = [[0, 1]];
const n = 6000;
const vis: boolean[][] = Array.from({ length: n }, () => [false, false]);
vis[0][1] = true;
for (let ans = 0; q.length; ++ans) {
for (let t = q.length; t; --t) {
const [i, k] = q.shift()!;
if (i === x) {
return ans;
}
const nxt: [number, number][] = [[i + a, 1]];
if (k & 1) {
nxt.push([i - b, 0]);
}
for (const [j, k] of nxt) {
if (j >= 0 && j < n && !s.has(j) && !vis[j][k]) {
vis[j][k] = true;
q.push([j, k]);
}
}
}
}
return -1;
}
Use this to step through a reusable interview workflow for this problem.
Two nested loops check every pair or subarray. The outer loop fixes a starting point, the inner loop extends or searches. For n elements this gives up to n²/2 operations. No extra space, but the quadratic time is prohibitive for large inputs.
Most array problems have an O(n²) brute force (nested loops) and an O(n) optimal (single pass with clever state tracking). The key is identifying what information to maintain as you scan: a running max, a prefix sum, a hash map of seen values, or two pointers.
Review these before coding to avoid predictable interview regressions.
Wrong move: Loop endpoints miss first/last candidate.
Usually fails on: Fails on minimal arrays and exact-boundary answers.
Fix: Re-derive loops from inclusive/exclusive ranges before coding.
Wrong move: Zero-count keys stay in map and break distinct/count constraints.
Usually fails on: Window/map size checks are consistently off by one.
Fix: Delete keys when count reaches zero.