题目描述
(中等)你有一个带有四个圆形拨轮的转盘锁。每个拨轮都有10个数字: '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'
。每个拨轮可以自由旋转:例如把 '9'
变为 '0'
,'0'
变为 '9'
。每次旋转都只能旋转一个拨轮的一位数字。
锁的初始数字为 '0000'
,一个代表四个拨轮的数字的字符串。
列表 deadends
包含了一组死亡数字,一旦拨轮的数字和列表里的任何一个元素相同,这个锁将会被永久锁定,无法再被旋转。
字符串 target
代表可以解锁的数字,你需要给出解锁需要的最小旋转次数,如果无论如何不能解锁,返回 -1 。
示例:
输入:deadends = ["0201","0101","0102","1212","2002"], target = "0202"
输出:6
解释:
可能的移动序列为 "0000" -> "1000" -> "1100" -> "1200" -> "1201" -> "1202" -> "0202"。
注意 "0000" -> "0001" -> "0002" -> "0102" -> "0202" 这样的序列是不能解锁的,
因为当拨动到 "0102" 时这个锁就会被锁定。
解题思路
主体上与【最小基因变化】一致,关键点在于模拟转盘的旋转,尤其是'0'
~'9'
之间char
类型的轮替。
int
型的0
~9
的循环可以依赖取余实现,
int fin = (fin + x) % 10;
考虑到char
存储ASCII码,可以仿照int
实现,
char fin = (fin - '0' + x) % 10 + '0';
由于本体转盘可以逆转,模拟过程中可能存在负数,可以考虑
char fin = (fin - '0' + x + 10) % 10 + '0';
代码实现
class Solution {
public:
int openLock(vector<string>& deadends, string target) {
//广度优先搜索
//hashset去重
unordered_set<string> visited;
for(string s : deadends){
visited.insert(s);
}
//若初始条件非法,返回-1
if(visited.count("0000")) return -1;
queue<string> que;
que.push("0000");
visited.insert("0000");
int ans = 0;
while(!que.empty()){
int size = que.size();
while(size--){
string temp = que.front();
que.pop();
if(temp == target) return ans;
//4个位置都可以旋转
for(int i = 0; i < 4; i++){
//每次只能旋转一个数字,或者不改变当前数字
for(int j = -1; j < 2; j++){
//回溯
string s = temp;
//模拟循环
s[i] = (s[i] - '0' + j + 10) % 10 + '0';
if(!visited.count(s)){
que.push(s);
visited.insert(s);
}
}
}
}
ans++;
}
return -1;
}
};
运行结果: