Opening the reading…
Opening the reading…
GRAPHS › DFS AND BFS ON GRAPHS
Open — the attempt gate is not wired up yet
This editorial is meant to unlock after you have run the problem at least once, with the worked solution behind one further deliberate click. That needs per-learner unlock state nothing stores today, so for now the whole article is open.
Try it yourself first →A lock code is a state, and one legal move changes exactly one wheel by one slot. Therefore two codes are connected when they differ in one position by one circular step. The task asks for the fewest moves from 0000 to the target, which is exactly the shortest-path distance between two states in an unweighted graph.
Breadth-first search explores every code reachable in zero moves, then every code reachable in one move, then every code reachable in two moves, and so on. The first time it reaches the target, no shorter route could have been skipped. Deadends are simply states that must never be entered, while a deadend at 0000 prevents the search from starting at all.
| MEASURE | BOUND | WHY |
|---|---|---|
| Time | O(N + m) | Building the deadend set processes m codes. BFS removes each reachable code from the queue once and tests eight neighbors, so the constant eight does not multiply the asymptotic bound; each code is also inserted into visited at most once. |
| Space | O(N + m) extra | The deadend set, visited set, and queue together can hold O(N + m) entries in the worst case. The returned integer is not output storage, and there is no separate output container; for this problem N is fixed at 10^4. |
#include <queue>
#include <string>
#include <unordered_set>
#include <vector>
using namespace std;
class Solution {
public:
int openLock(vector<string>& deadends, string target) {
unordered_set<string> deadSet(deadends.begin(), deadends.end());
if (deadSet.count("0000")) {
return -1;
}
if (target == "0000") {
return 0;
}
queue<string> q;
unordered_set<string> visited;
q.push("0000");
visited.insert("0000");
int steps = 0;
while (!q.empty()) {
int levelSize = static_cast<int>(q.size());
++steps;
for (int i = 0; i < levelSize; ++i) {
string cur = q.front();
q.pop();
for (int j = 0; j < 4; ++j) {
char original = cur[j];
for (int delta : {-1, 1}) {
char nextDigit = static_cast<char>(
(original - '0' + delta + 10) % 10 + '0'
);
cur[j] = nextDigit;
if (cur == target) {
return steps;
}
if (!visited.count(cur) && !deadSet.count(cur)) {
visited.insert(cur);
q.push(cur);
}
}
cur[j] = original;
}
}
}
return -1;
}
};The levelSize snapshot is the line that gives steps its meaning: every code removed during this loop was reached with the same number of moves. The restore assignment is equally important because each of the eight trials must start from the unchanged current code, not from the digit modified by the previous trial.