算法练习(16) —— Open the Lock
习题
本题取自 leetcode 中的 Breadth-first Search
栏目中的第752题:
Open the Lock
题目如下:
Description
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.Note
- The length of
deadends
will be in the range[1, 500]
.target
will not be in the listdeadends
.- Every string in
deadends
and the stringtarget
will be a string of 4 digits from the 10,000 possibilities'0000'
to'9999'
.Example1
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”.Example2
Input: deadends = [“8888”], target = “0009”
Output: 1
Explanation:
We can turn the last wheel in reverse to move from “0000” -> “0009”.Example3
Input: deadends = [“8887”,”8889”,”8878”,”8898”,”8788”,”8988”,”7888”,”9888”], target = “8888”
Output: -1
Explanation:
We can’t reach the target without getting stuck.Example4
Input: deadends = [“0000”], target = “8888”
Output: -1
思考与代码
- 首先理解题意,锁的每个数字可以从0到9自由转动,其中0和9相连,从9往下转就得到了0。默认从
"0000"
开始,要求算转到目标数字的最少数目。其中还有一些死锁点,一旦转到那些数字,锁就坏了,所以必须要避开那些点。当然如果无法到达就返回-1. - 首先可以确定,每一次转动数字的时候,有2种转法:往前转和往后转。而一共四个数字,所以就有2*4种转法。很自然的可以想到用广搜的思想,对每个从队列中弹出的点分别尝试这些转法,可行的话插入列队尾,步数+1。最终自然能找到最小的步数。
- 这里除了注意算法之外,还要注意一些超时的问题。首先是创建新的vector用于存储转法时,可以采用拷贝函数的方法。其次是容器选择方面,用vector的迭代器作find,速度会过慢,导致超时…这时候可以采用c++11中的unordered_set,它是基于哈希表的,使用find函数的时候速度会提升超级多。如果是java语言的话,就可以采用hashset。不得不说,论做题的3大困扰,RE,WA,TLE,还是TLE最惹人烦吧
代码如下:
#include <string>
#include <vector>
#include <queue>
using namespace std;
class Solution {
private:
// find if the input string does in the vector
bool findString(vector<string>& strings, string s) {
vector<string>::iterator res = find(strings.begin(), strings.end(), s);
if (res == strings.end())
return false;
return true;
}
// get neighbor strings of the input string
vector<string> neighbors(string s) {
vector<string> res;
for (int i = 0; i < 4; i++) {
string temp = s;
temp[i] = (s[i] - '0' + 1) % 10 + '0';
res.push_back(temp);
temp[i] = (s[i] - '0' + 9) % 10 + '0';
res.push_back(temp);
}
return res;
}
public:
int openLock(vector<string>& deadends, string target) {
// initialize
vector<string> visited;
queue<string> q;
int steps = 0;
string start = "0000";
visited.push_back(start);
q.push(start);
while (!q.empty()) {
int len = q.size();
for (int i = 0; i < len; i++) {
string top = q.front();
q.pop();
vector<string> neigh(neighbors(top));
for (auto s : neigh) {
if (s == target)
return ++steps;
if (findString(visited, s))
continue;
if (!findString(deadends, s)) {
q.push(s);
visited.push_back(s);
}
}
}
steps++;
}
return -1;
}
};