1. 程式人生 > >[LeetCode] Open the Lock 開鎖

[LeetCode] Open the Lock 開鎖

span 方法 stuck 什麽 c++ ava 記錄 死鎖 imu

You have a lock in front of you with 4 circular wheels. Each wheel has 10 slots: ‘0‘, ‘1‘, ‘2‘, ‘3‘, ‘4‘, ‘5‘, ‘6‘, ‘7‘, ‘8‘, ‘9‘. The wheels can rotate freely and wrap around: for example we can turn ‘9‘ to be ‘0‘, or ‘0‘ to be ‘9‘. Each move consists of turning one wheel one slot.

The lock initially starts at ‘0000‘

, a string representing the state of the 4 wheels.

You are given a list of deadends dead ends, meaning if the lock displays any of these codes, the wheels of the lock will stop turning and you will be unable to open it.

Given a target representing the value of the wheels that will unlock the lock, return the minimum total number of turns required to open the lock, or -1 if it is impossible.

Example 1:

Input: deadends = ["0201","0101","0102","1212","2002"], target = "0202"
Output: 6
Explanation:
A sequence of valid moves would be "0000" -> "1000" -> "1100" -> "1200" -> "1201" -> "1202" -> "0202".
Note that a sequence like "0000" -> "0001" -> "0002" -> "0102" -> "0202" would be invalid,
because the wheels of the lock become stuck after the display becomes the dead end "0102".

Example 2:

Input: deadends = ["8888"], target = "0009"
Output: 1
Explanation:
We can turn the last wheel in reverse to move from "0000" -> "0009".

Example 3:

Input: deadends = ["8887","8889","8878","8898","8788","8988","7888","9888"], target = "8888"
Output: -1
Explanation:
We can‘t reach the target without getting stuck.

Example 4:

Input: deadends = ["0000"], target = "8888"
Output: -1

Note:

  1. The length of deadends will be in the range [1, 500].
  2. target will not be in the list deadends.
  3. Every string in deadends and the string target will be a string of 4 digits from the 10,000 possibilities ‘0000‘ to ‘9999‘.

這道題說有一種可滑動的四位數的鎖,貌似行李箱上比較常見這種鎖。給了我們一個目標值,還有一些死鎖的情況,就是說如果到達這些死鎖的位置,就不能再動了,相當於迷宮中的障礙物。然後問我們最少多少步可以從初始的0000位置滑動到給定的target位置。如果各位足夠老辣的話,應該能發現其實本質就是個迷宮遍歷的問題,只不過相鄰位置不再是上下左右四個位置,而是四位數字每個都加一減一,總共有八個相鄰的位置。遍歷迷宮問題中求最短路徑要用BFS來做,那麽這道題也就是用BFS來解啦,和經典BFS遍歷迷宮解法唯一不同的就是找下一個位置的地方,這裏我們要遍歷四位數字的每一位,然後分別加1減1,我們用j從-1遍歷到1,遇到0跳過,也就是實現了加1減1的過程。然後我們要計算要更新位上的數字,為了處理9加1變0,和0減1變9的情況,我們統一給該位數字加上個10,然後再加或減1,最後再對10取余即可,註意字符和整型數之間通過加或減‘0‘來轉換。我們用結果res來記錄BFS遍歷的層數,如果此時新生成的字符串等於target了,直接返回結果res,否則我們看如果該字符串不在死鎖集合裏,且之前沒有遍歷過,那麽加入隊列queue中,之後將該字符串加入visited集合中,參見代碼如下:

解法一:

class Solution {
public:
    int openLock(vector<string>& deadends, string target) {
        unordered_set<string> deadlock(deadends.begin(), deadends.end());
        if (deadlock.count("0000")) return -1;
        int res = 0;
        unordered_set<string> visited{{"0000"}};
        queue<string> q{{"0000"}};
        while (!q.empty()) {
            ++res;
            for (int k = q.size(); k > 0; --k) {
                auto t = q.front(); q.pop();
                for (int i = 0; i < t.size(); ++i) {
                    for (int j = -1; j <= 1; ++j) {
                        if (j == 0) continue;
                        string str = t;
                        str[i] = ((t[i] - 0) + 10 + j) % 10 + 0;
                        if (str == target) return res;
                        if (!visited.count(str) && !deadlock.count(str)) q.push(str);        
                        visited.insert(str);
                    }
                }
            }
        }
        return -1;
    }
};

下面這種方法也是用的BFS遍歷,不同之處在於生成新字符串的方法,這裏我們采用拼接法來生成新字符串,而不是像上面那樣使用置換字符串的方法。我們對於加一和減一分別進行拼接,註意處理9加1變0,和0減1變9的情況。然後剩下的部分就和經典的BFS遍歷寫法沒有什麽太大的區別了,參見代碼如下:

解法二:

class Solution {
public:
    int openLock(vector<string>& deadends, string target) {
        unordered_set<string> deadlock(deadends.begin(), deadends.end());
        if (deadlock.count("0000")) return -1;
        int res = 0;
        unordered_set<string> visited{{"0000"}};
        queue<string> q{{"0000"}};
        while (!q.empty()) {
            ++res;
            for (int k = q.size(); k > 0; --k) {
                auto t = q.front(); q.pop();
                for (int i = 0; i < t.size(); ++i) {
                    char c = t[i];
                    string str1 = t.substr(0, i) + to_string(c == 9 ? 0 : c - 0 + 1) + t.substr(i + 1);
                    string str2 = t.substr(0, i) + to_string(c == 0 ? 9 : c - 0 - 1) + t.substr(i + 1);
                    if (str1 == target || str2 == target) return res;
                    if (!visited.count(str1) && !deadlock.count(str1)) q.push(str1);
                    if (!visited.count(str2) && !deadlock.count(str2)) q.push(str2);
                    visited.insert(str1);
                    visited.insert(str2);
                }
            }
        }
        return -1;
    }
};

參考資料:

https://leetcode.com/problems/open-the-lock/discuss/110230/BFS-solution-C++

https://leetcode.com/problems/open-the-lock/discuss/110237/Regular-java-BFS-solution-and-2-end-BFS-solution-with-improvement

LeetCode All in One 題目講解匯總(持續更新中...)

[LeetCode] Open the Lock 開鎖