#include <queue>
#include <string>
#include <vector>

class Solution {
  public:
    int openLock(const std::vector<std::string> &deadends,
                 const std::string &target) {
        std::vector<bool> visited(10000);
        for (const auto &end : deadends) {
            visited[std::stoi(end)] = true;
        }

        // we're already starting with a dead end
        if (visited[0]) {
            return -1;
        }

        auto t = std::stoi(target);

        std::queue<std::pair<int, int>> q;
        q.emplace(0, 0);
        visited[0] = true;

        while (!q.empty()) {
            auto [steps, comb] = q.front();
            q.pop();

            // found the target
            if (comb == t) {
                return steps;
            }

            auto comb_tmp = comb;
            for (auto d = 1; d < 10000; d *= 10) {
                auto digit = comb_tmp % 10;
                comb_tmp /= 10;

                for (auto i : {-1, 1}) {
                    auto next_digit = (digit + i + 10) % 10;
                    auto next_comb = comb + (next_digit - digit) * d;

                    if (!visited[next_comb]) {
                        q.emplace(steps + 1, next_comb);
                        visited[next_comb] = true;
                    }
                }
            }
        }

        return -1;
    }
};