3629. Minimum Jumps to Reach End via Prime Teleportation

  • Array
  • BFS
  • Number Theory
  • Hash Table
TIME
O(M log log M + n log M), M = max(nums)
SPACE
O(M + n log M)
problem on leetcode ↗source on github ↗

Intuition

Every move costs one jump, so this is a shortest-path problem on indices. Adjacent moves are simple, but teleport moves can be large: from a prime value p, we can jump to every index whose value is divisible by p.

The key optimization is to preprocess teleport destinations by prime divisor. Then BFS can expand a prime teleport group once and clear it, because expanding the same prime again would only repeat work.

Approach

  1. Build the smallest-prime-factor array up to max(nums).
  2. Identify which values in nums are prime; only those primes can be used as teleport sources.
  3. For each index i, factor nums[i] into distinct prime factors.
  4. If a factor p appears as a prime value in nums, add index i to teleports[p].
  5. Run BFS from index 0.
  6. From each index:
    • visit i - 1
    • visit i + 1
    • if nums[i] is prime, visit every index in teleports[nums[i]]
  7. After using a prime teleport list, clear it so it is never scanned again.

Complexity

  • Time: O(M log log M + n log M), where M = max(nums)
  • Space: O(M + n log M), for the smallest-prime-factor array and teleport lists

Edge Cases

  • n = 1 -> already at the target
  • No prime values -> only adjacent steps are possible
  • Prime at current index has no useful destination -> BFS still continues through adjacent steps
  • Repeated values and repeated prime factors -> each index is added once per distinct relevant factor

Code

from collections import defaultdict, deque
from typing import List


class Solution:
    def minJumps(self, nums: List[int]) -> int:
        """
        3629. Minimum Jumps to Reach End via Prime Teleportation
        Time: O(M log log M + n log M), M = max(nums)
        Space: O(M + n log M)
        """
        n = len(nums)
        if n == 1:
            return 0

        max_value = max(nums)
        spf = self._build_spf(max_value)
        prime_sources = {num for num in nums if self._is_prime(num, spf)}

        teleports = defaultdict(list)
        for i, num in enumerate(nums):
            for factor in self._prime_factors(num, spf):
                if factor in prime_sources:
                    teleports[factor].append(i)

        distance = [-1] * n
        distance[0] = 0
        queue = deque([0])

        while queue:
            i = queue.popleft()
            next_distance = distance[i] + 1

            for nei in (i - 1, i + 1):
                if 0 <= nei < n and distance[nei] == -1:
                    if nei == n - 1:
                        return next_distance
                    distance[nei] = next_distance
                    queue.append(nei)

            value = nums[i]
            if value in prime_sources:
                for nei in teleports[value]:
                    if nei != i and distance[nei] == -1:
                        if nei == n - 1:
                            return next_distance
                        distance[nei] = next_distance
                        queue.append(nei)
                teleports[value].clear()

        return distance[n - 1]

    def _build_spf(self, max_value: int) -> List[int]:
        spf = list(range(max_value + 1))
        if max_value >= 0:
            spf[0] = 0
        if max_value >= 1:
            spf[1] = 1

        factor = 2
        while factor * factor <= max_value:
            if spf[factor] == factor:
                for multiple in range(factor * factor, max_value + 1, factor):
                    if spf[multiple] == multiple:
                        spf[multiple] = factor
            factor += 1

        return spf

    def _is_prime(self, num: int, spf: List[int]) -> bool:
        return num >= 2 and spf[num] == num

    def _prime_factors(self, num: int, spf: List[int]) -> List[int]:
        factors = []

        while num > 1:
            factor = spf[num]
            factors.append(factor)
            while num % factor == 0:
                num //= factor

        return factors