Given an array of integers arr, you are initially positioned at the first index of the array.

In one step you can jump from index i to index:

  • i + 1 where: i + 1 < arr.length.
  • i - 1 where: i - 1 >= 0.
  • j where: arr[i] == arr[j] and i != j.

Return the minimum number of steps to reach the last index of the array.

Notice that you can not jump outside of the array at any time.

Example

Input: arr = [100,-23,-23,404,100,23,23,23,3,404]
Output: 3
Explanation: You need three jumps from index 0 --> 4 --> 3 --> 9. Note that index 9 is the last index of the array.

Solution

Great solution found here.

/**
 * @param {number[]} arr
 * @return {number}
 */
var minJumps = function (arr) {
  // build a map of value to indexes
  const targetIndex = arr.length - 1;
  if (targetIndex < 1) return 0;

  const map = new Map();
  for (let i = 0; i < arr.length; i++) {
    // if (i == targetIndex) continue;
    if (!map.has(arr[i])) map.set(arr[i], []);
    map.get(arr[i]).push(i);
  }
  // start from the end. check if the value is the same as the target. if so, return 1
  let checkedIndexes = new Set();
  let stepCount = 0;
  let currentStepIndexes = new Set();
  currentStepIndexes.add(targetIndex);
  // from right to left. end to start. arr.length - 1 to 0
  while (currentStepIndexes.size > 0) {
    let nextStepIndexes = new Set();
    for (const currentStepIndex of currentStepIndexes.values()) {
      checkedIndexes.add(currentStepIndex);

      if (currentStepIndex == 0) return stepCount;
      // -1
      if (currentStepIndex > 0 && !checkedIndexes.has(currentStepIndex - 1)) {
        if (currentStepIndex - 1 == 0) return ++stepCount;
        nextStepIndexes.add(currentStepIndex - 1);
      }
      // jumps
      const jumps = map.get(arr[currentStepIndex]);
      if (jumps) {
        map.delete(arr[currentStepIndex]);
        let limit = 0;
        for (const jumpIndex of jumps) {
          // if (jumpIndex == targetIndex) return ++stepCount;
          if (
            jumpIndex !== currentStepIndex &&
            !checkedIndexes.has(jumpIndex)
          ) {
            limit++;
            if (limit > 5) break;
            if (jumpIndex == 0) return ++stepCount;
            nextStepIndexes.add(jumpIndex);
          }
        }
      }
      // +1
      if (
        currentStepIndex < targetIndex &&
        !checkedIndexes.has(currentStepIndex + 1)
      ) {
        nextStepIndexes.add(currentStepIndex + 1);
      }
    }
    currentStepIndexes = nextStepIndexes;
    stepCount++;
  }
  return stepCount;
};