diff --git a/leetcode/stuff.py b/leetcode/stuff.py index 361c704..7174759 100644 --- a/leetcode/stuff.py +++ b/leetcode/stuff.py @@ -286,3 +286,71 @@ def binary_tree_from_preorder_inorder( split_pos = inorder.index(preorder[0]) return subtree(inorder[:split_pos], inorder[split_pos + 1 :]) + + +class JumpSpace(typing.NamedTuple): + pos: int + val: int + moves: list["JumpSpace"] + + @classmethod + def from_board( + cls, pos: int = 0, board: typing.Iterable[int] = () + ) -> typing.Optional["JumpSpace"]: + board = list(board) + + if len(board) == 0: + return None + + space = cls(pos, board[pos], []) + space.collect(board) + return space + + def collect(self, board: list[int]) -> None: + del self.moves[:] + + if self.pos > len(board) or len(board) == 0: + return + + for n in range(self.pos + 1, self.pos + self.val + 1): + if n >= len(board): + break + + self.moves.append(typing.cast(JumpSpace, JumpSpace.from_board(n, board))) + + def jump_paths(self) -> list[list[int]]: + ret: list[list[int]] = [[self.pos]] + + for next_space in self.moves: + for path in next_space.jump_paths(): + ret.append([self.pos] + path) + + return ret + + +def count_min_jumps_from_board(board: list[int]) -> int: + if len(board) == 1: + return 0 + + complete_paths = collect_complete_jump_paths_from_board(board) + + if len(complete_paths) == 0: + return -1 + + return min([len(p) - 1 for p in complete_paths]) + + +def collect_complete_jump_paths_from_board(board: list[int]) -> list[list[int]]: + return [ + p + for p in collect_jump_paths_from_board(board) + if len(p) > 0 and p[-1] >= len(board) - 1 + ] + + +def collect_jump_paths_from_board(board: list[int]) -> list[list[int]]: + space = JumpSpace.from_board(0, board) + if space is None: + return [] + + return space.jump_paths() diff --git a/leetcode/test_stuff.py b/leetcode/test_stuff.py index 2959a7d..bb1c98b 100644 --- a/leetcode/test_stuff.py +++ b/leetcode/test_stuff.py @@ -249,3 +249,48 @@ def test_binary_tree_from_preorder_inorder( preorder: list[int], inorder: list[int], expected: stdlib.BinaryTreeNode ): assert stuff.binary_tree_from_preorder_inorder(preorder, inorder) == expected + + +@pytest.mark.parametrize( + ("board", "expected"), + [ + ( + [2, 3, 1, 1, 4], + [ + [0, 1, 2, 3, 4], + [0, 1, 3, 4], + [0, 1, 4], + [0, 2, 3, 4], + ], + ), + ], +) +def test_collect_complete_jump_paths_from_board( + board: list[int], expected: list[list[int]] +): + assert list(sorted(stuff.collect_complete_jump_paths_from_board(board))) == expected + + +@pytest.mark.parametrize( + ("board", "expected"), + [ + ( + [2, 3, 1, 1, 4], + 2, + ), + ( + [2, 3, 0, 1, 4], + 2, + ), + ( + [1], + 0, + ), + ( + [1, 2], + 1, + ), + ], +) +def test_count_min_jumps_from_board(board: list[int], expected: int): + assert stuff.count_min_jumps_from_board(board) == expected