Shubham Sourabhthe room · director's cutplain résumé
Room 04 · The Grind — Graphs

Open The Lock

MEDIUM✓ CHIP-TIMEDLC #752 — FULL STATEMENT

The briefingthe full drill, in my own words

The drill: A 4-wheel combination lock starts at 0000. Each move turns one wheel one notch either way. Find the fewest moves to reach a target combination, given a list of combinations the lock jams on and refuses to pass through.

This drill models a 4-wheel combination lock, each wheel holding a digit 0 through 9 that wraps around. A single move turns exactly one wheel one notch in either direction, and the lock starts sitting at 0000.

A list of forbidden combinations acts as deadends — the lock simply cannot land on one of those combinations at any point, including as the very first move from the start. A deadend list that includes the start itself, or that walls off the target completely, blocks the lock for good.

The task is the fewest moves needed to dial in a given target combination, starting from 0000, or −1 if no sequence of legal moves ever reaches it without passing through a deadend.

  • deadends list can include the start itself, blocking everything immediately
  • each wheel wraps: turning past 9 lands on 0, and past 0 lands on 9
  • at most a few thousand deadend combinations
  • answer is the minimum move count, or −1 if the target is unreachable
EX 01
deadends = [] · target = "0000"
→ 0
already at target
EX 02
deadends = [] · target = "0007"
→ 3
shorter to turn one wheel down than up
EX 03
deadends = [] · target = "9999"
→ 4
every wheel one notch backward

The hintstake only what you need

HINT 1 THE NUDGE

Every combination is a node, and one wheel-turn is an edge to a neighbor. Fewest moves to a target, in an unweighted graph, is a shortest-path question — what search explores layer by layer?

HINT 2 THE STRUCTURE

BFS from 0000, skipping deadends and anything already visited, finds the shortest path — but it explores the whole radius of the search outward from a single start.

HINT 3 ONE STEP FROM THE ANSWER

Grow two frontiers at once — one from 0000, one from the target — and stop the instant they touch. Each side only needs to cover half the distance.

Coach's boardthe pattern, step by step

THE TWO-SIDED DIALPATTERN · BIDIRECTIONAL BFSstart 0000 → target 0009 · deadend 8888 (unrelated)
STEP 1

Lock at 0000, target 0009 — deadend 8888 isn't even near this path. Bidirectional BFS grows a frontier from each side at once.

STEP 1 / 5 · ← → WORK TOO

The splitstwo paces, two languages

grind/open-the-lock.py — RACE PACE
LANG ▸
PACE ▸
class Solution:
    def openLock(self, deadends: List[str], target: str) -> int:
        dead = set(deadends)
        start = "0000"
        if start in dead or target in dead:
            return -1
        if start == target:
            return 0

        def neighbors(state):
            for i in range(4):
                d = int(state[i])
                for delta in (1, -1):
                    nd = (d + delta) % 10
                    yield state[:i] + str(nd) + state[i + 1:]

        front = {start}
        back = {target}
        seen = {start, target}
        steps = 0
        while front and back:
            if len(front) > len(back):
                front, back = back, front
            nxt_front = set()
            for state in front:
                for nxt in neighbors(state):
                    if nxt in dead:
                        continue
                    if nxt in back:
                        return steps + 1
                    if nxt not in seen:
                        seen.add(nxt)
                        nxt_front.add(nxt)
            front = nxt_front
            steps += 1
        return -1
TIME O(10^4) STATES, ROUGHLY HALF EXPLOREDSPACE O(10^4)PYTHON · RACE PACE · 36 LN

✓ CHIP-TIMEDALL 4 SOLUTIONS RAN GREEN AGAINST SELF-AUTHORED CASES IN CI · JDK 21 · CPYTHON 3.12 · NOTHING PUBLISHES RED