Minimum Jumps to Reach End via Prime Teleportation

medium breadth-first search number theory prime factors hash table

Problem

You start at index 0 of an array nums of length n and want to reach index n − 1 in as few jumps as possible. From index i you can take an adjacent step to i + 1 or i − 1 (if in bounds). And if nums[i] is a prime p, you may teleport to any index j ≠ i with nums[j] % p == 0. Return the minimum number of jumps.

Inputnums = [1, 2, 4, 6]
Output2
Step 0 → 1. At index 1, nums[1] = 2 is prime, so teleport to index 3 since 6 % 2 == 0. Two jumps total.

def minJumps(nums):
    n = len(nums)
    if n == 1:
        return 0
    mx = max(nums)
    spf = list(range(mx + 1))           # smallest prime factor sieve
    i = 2
    while i * i <= mx:
        if spf[i] == i:                 # i is prime
            for j in range(i * i, mx + 1, i):
                if spf[j] == j:
                    spf[j] = i
        i += 1
    # bucket[p] = indices j with nums[j] % p == 0
    bucket = {}
    for j, v in enumerate(nums):
        x = v
        while x > 1:                     # distinct prime factors of v
            p = spf[x]
            bucket.setdefault(p, []).append(j)
            while x % p == 0:
                x //= p
    dist = [-1] * n
    dist[0] = 0
    queue = [0]
    while queue:
        i = queue.pop(0)
        if i == n - 1:
            return dist[i]
        for nb in (i - 1, i + 1):       # adjacent steps
            if 0 <= nb < n and dist[nb] == -1:
                dist[nb] = dist[i] + 1
                queue.append(nb)
        p = nums[i]
        if spf[p] == p and p > 1 and p in bucket:   # nums[i] is prime
            for nb in bucket[p]:        # teleport to every multiple of p
                if dist[nb] == -1:
                    dist[nb] = dist[i] + 1
                    queue.append(nb)
            del bucket[p]               # visit each prime bucket once
    return dist[n - 1]
function minJumps(nums) {
  const n = nums.length;
  if (n === 1) return 0;
  const mx = Math.max(...nums);
  const spf = Array.from({ length: mx + 1 }, (_, k) => k); // smallest prime factor
  for (let i = 2; i * i <= mx; i++) {
    if (spf[i] === i) {                 // i is prime
      for (let j = i * i; j <= mx; j += i) {
        if (spf[j] === j) spf[j] = i;
      }
    }
  }
  // bucket[p] = indices j with nums[j] % p === 0
  const bucket = new Map();
  for (let j = 0; j < n; j++) {
    let x = nums[j];
    while (x > 1) {                      // distinct prime factors
      const p = spf[x];
      if (!bucket.has(p)) bucket.set(p, []);
      bucket.get(p).push(j);
      while (x % p === 0) x = Math.floor(x / p);
    }
  }
  const dist = new Array(n).fill(-1);
  dist[0] = 0;
  const queue = [0];
  let head = 0;
  while (head < queue.length) {
    const i = queue[head++];
    if (i === n - 1) return dist[i];
    for (const nb of [i - 1, i + 1]) {  // adjacent steps
      if (nb >= 0 && nb < n && dist[nb] === -1) {
        dist[nb] = dist[i] + 1;
        queue.push(nb);
      }
    }
    const p = nums[i];
    if (spf[p] === p && p > 1 && bucket.has(p)) { // nums[i] is prime
      for (const nb of bucket.get(p)) { // teleport to each multiple
        if (dist[nb] === -1) {
          dist[nb] = dist[i] + 1;
          queue.push(nb);
        }
      }
      bucket.delete(p);                 // visit each bucket once
    }
  }
  return dist[n - 1];
}
int minJumps(int[] nums) {
    int n = nums.length;
    if (n == 1) return 0;
    int mx = 0;
    for (int v : nums) mx = Math.max(mx, v);
    int[] spf = new int[mx + 1];        // smallest prime factor sieve
    for (int k = 0; k <= mx; k++) spf[k] = k;
    for (int i = 2; (long) i * i <= mx; i++) {
        if (spf[i] == i) {              // i is prime
            for (int j = i * i; j <= mx; j += i)
                if (spf[j] == j) spf[j] = i;
        }
    }
    // bucket[p] = indices j with nums[j] % p == 0
    Map<Integer, List<Integer>> bucket = new HashMap<>();
    for (int j = 0; j < n; j++) {
        int x = nums[j];
        while (x > 1) {                  // distinct prime factors
            int p = spf[x];
            bucket.computeIfAbsent(p, z -> new ArrayList<>()).add(j);
            while (x % p == 0) x /= p;
        }
    }
    int[] dist = new int[n];
    Arrays.fill(dist, -1);
    dist[0] = 0;
    Deque<Integer> queue = new ArrayDeque<>();
    queue.add(0);
    while (!queue.isEmpty()) {
        int i = queue.poll();
        if (i == n - 1) return dist[i];
        for (int nb : new int[]{i - 1, i + 1}) {   // adjacent steps
            if (nb >= 0 && nb < n && dist[nb] == -1) {
                dist[nb] = dist[i] + 1;
                queue.add(nb);
            }
        }
        int p = nums[i];
        if (spf[p] == p && p > 1 && bucket.containsKey(p)) { // prime
            for (int nb : bucket.get(p)) {          // teleport
                if (dist[nb] == -1) {
                    dist[nb] = dist[i] + 1;
                    queue.add(nb);
                }
            }
            bucket.remove(p);           // visit each bucket once
        }
    }
    return dist[n - 1];
}
int minJumps(vector<int>& nums) {
    int n = nums.size();
    if (n == 1) return 0;
    int mx = *max_element(nums.begin(), nums.end());
    vector<int> spf(mx + 1);             // smallest prime factor sieve
    for (int k = 0; k <= mx; k++) spf[k] = k;
    for (int i = 2; (long long) i * i <= mx; i++) {
        if (spf[i] == i) {              // i is prime
            for (int j = i * i; j <= mx; j += i)
                if (spf[j] == j) spf[j] = i;
        }
    }
    // bucket[p] = indices j with nums[j] % p == 0
    unordered_map<int, vector<int>> bucket;
    for (int j = 0; j < n; j++) {
        int x = nums[j];
        while (x > 1) {                  // distinct prime factors
            int p = spf[x];
            bucket[p].push_back(j);
            while (x % p == 0) x /= p;
        }
    }
    vector<int> dist(n, -1);
    dist[0] = 0;
    queue<int> q;
    q.push(0);
    while (!q.empty()) {
        int i = q.front(); q.pop();
        if (i == n - 1) return dist[i];
        for (int nb : {i - 1, i + 1}) { // adjacent steps
            if (nb >= 0 && nb < n && dist[nb] == -1) {
                dist[nb] = dist[i] + 1;
                q.push(nb);
            }
        }
        int p = nums[i];
        if (spf[p] == p && p > 1 && bucket.count(p)) { // prime
            for (int nb : bucket[p]) {  // teleport
                if (dist[nb] == -1) {
                    dist[nb] = dist[i] + 1;
                    q.push(nb);
                }
            }
            bucket.erase(p);            // visit each bucket once
        }
    }
    return dist[n - 1];
}
Time: O(n log M + M log log M) Space: O(n + M)