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'.

Hint:

We can think of this problem as a shortest path problem on a graph: there are `10000` nodes (strings `'0000'` to `'9999'`), and there is an edge between two nodes if they differ in one digit, that digit differs by 1 (wrapping around, so `'0'` and `'9'` differ by 1), and if *both* nodes are not in `deadends`.

有一个可转动的四位数的密码锁,给一个目标值,还有一些锁死的情况,出现死锁的位置,就不能再动了,相当于迷宫中的障碍物。问最少多少步可以从初始的0000位置转动到给定的target位置。

可以把问题想成图的最短路径问题,有10000个节点(字符串‘0000'到'9999'),如果两个节点之间相差1并且不在锁死节点中,则它们之间有一个边。类似于迷宫遍历的问题,只不过相邻位置不再是上下左右四个位置,而是四位数字每个都加一减一,总共有八个相邻的位置。

解法:BFS

Java:

public static int openLock(String[] deadends, String target) {
Queue<String> q = new LinkedList<>();
Set<String> deads = new HashSet<>(Arrays.asList(deadends));
Set<String> visited = new HashSet<>(); int depth = 0;
String marker = "*";
q.addAll(Arrays.asList("0000", "*"));
while(!q.isEmpty()) {
String node = q.poll();
if(node.equals(target))
return depth;
if(visited.contains(node) || deads.contains(node))
continue;
if(node.equals(marker) && q.isEmpty())
return -1;
if(node.equals(marker)) {
q.add(marker);
depth += 1;
} else {
visited.add(node);
q.addAll(getSuccessors(node));
}
}
return depth;
} private static List<String> getSuccessors(String str) {
List<String> res = new LinkedList<>();
for (int i = 0; i < str.length(); i++) {
res.add(str.substring(0, i) + (str.charAt(i) == '0' ? 9 : str.charAt(i) - '0' - 1) + str.substring(i+1));
res.add(str.substring(0, i) + (str.charAt(i) == '9' ? 0 : str.charAt(i) - '0' + 1) + str.substring(i+1));
}
return res;
}  

Python:

Shortest path finding, when the weights are constant, as in this case = 1, BFS is the best way to go.

Best way to avoid TLE is by using deque and popleft() .
[Using list() and pop(0) is a linear operation in Python, resulting in TLE]

class Solution(object):
def openLock(self, deadends, target):
"""
:type deadends: List[str]
:type target: str
:rtype: int
"""
marker, depth = 'x', 0
visited, q, deadends = set(), deque(['0000', marker]), set(deadends) while q:
node = q.popleft()
if node == target:
return depth
if node in visited or node in deadends:
continue
if node == marker and not q:
return -1
if node == marker:
q.append(marker)
depth += 1
else:
visited.add(node)
q.extend(self.successors(node))
return -1 def successors(self, src):
res = []
for i, ch in enumerate(src):
num = int(ch)
res.append(src[:i] + str((num - 1) % 10) + src[i+1:])
res.append(src[:i] + str((num + 1) % 10) + src[i+1:])
return res  

Python:

# Time:  O(k * n^k + d), n is the number of alphabets,
# k is the length of target,
# d is the size of deadends
# Space: O(k * n^k + d)
class Solution(object):
def openLock(self, deadends, target):
"""
:type deadends: List[str]
:type target: str
:rtype: int
"""
dead = set(deadends)
q = ["0000"]
lookup = {"0000"}
depth = 0
while q:
next_q = []
for node in q:
if node == target: return depth
if node in dead: continue
for i in xrange(4):
n = int(node[i])
for d in (-1, 1):
nn = (n+d) % 10
neighbor = node[:i] + str(nn) + node[i+1:]
if neighbor not in lookup:
lookup.add(neighbor)
next_q.append(neighbor)
q, next_q = next_q, []
depth += 1
return -1  

C++:

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;
}
};

C++:  

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;
}
};

  

All LeetCode Questions List 题目汇总

[LeetCode] 752. Open the Lock 开锁的更多相关文章

  1. [LeetCode] Open the Lock 开锁

    You have a lock in front of you with 4 circular wheels. Each wheel has 10 slots: '0', '1', '2', '3', ...

  2. LeetCode 752. Open the Lock

    原题链接在这里:https://leetcode.com/problems/open-the-lock/ 题目: You have a lock in front of you with 4 circ ...

  3. java模拟开锁

    java模拟开锁 service qq:928900200 Introduction to Computer Science II: CSCI142Fall 2014Lab #1Instructor: ...

  4. Java并发编程之Lock(同步锁、死锁)

    这篇文章是接着我上一篇文章来的. 上一篇文章 同步锁 为什么需要同步锁? 首先,我们来看看这张图. 这是一个程序,多个对象进行抢票. package MovieDemo; public class T ...

  5. [C#基础]说说lock到底锁谁?

    写在前面 最近一个月一直在弄文件传输组件,其中用到多线程的技术,但有的地方确实需要只能有一个线程来操作,如何才能保证只有一个线程呢?首先想到的就是锁的概念,最近在我们项目组中听的最多的也是锁谁,如何锁 ...

  6. Coarse-Grained lock 粗粒度锁

    用一个锁Lock一组相关的对象 有时,需要按组来修改多个对象. 这样,在需要锁住其中一个的时候,必须连带地将其他的对象都上锁. 为每一个对象都加上一个锁是很繁琐的. 粗粒度锁是覆盖多个对象的单个锁. ...

  7. 4位开锁<dfs>

    题意: 有一个四位密码的锁,每一位是1~9的密码,1跟9相连.并且相邻的连个密码位可以交换.每改变一位耗时1s,给出锁的当前状态和密码,求最少解锁时间. 思路: 用bfs枚举出所有相邻交换的情况,并记 ...

  8. Java 线程锁机制 -Synchronized Lock 互斥锁 读写锁

    (1)synchronized 是互斥锁: (2)ReentrantLock 顾名思义 :可重入锁 (3)ReadWriteLock :读写锁 读写锁特点: a)多个读者可以同时进行读b)写者必须互斥 ...

  9. hihocoder 1075 : 开锁魔法III

    描述 一日,崔克茜来到小马镇表演魔法. 其中有一个节目是开锁咒:舞台上有 n 个盒子,每个盒子中有一把钥匙,对于每个盒子而言有且仅有一把钥匙能打开它.初始时,崔克茜将会随机地选择 k 个盒子用魔法将它 ...

随机推荐

  1. 微服务:springboot与swagger2的集成

    现在测试都提倡自动化测试,那我们作为后台的开发人员,也得进步下啊,以前用postman来测试后台接口,那个麻烦啊,一个字母输错就导致测试失败,现在swagger的出现可谓是拯救了这些开发人员,便捷之处 ...

  2. sass环境搭建之node-sass,ruby

    该内容全部为搬运,感谢作者的分享~,附有原文链接. 使用ruby环境 SASS学习系列之(一)--------- SASS,SCSS环境搭建(Ruby) 使用node-sass SASS学习系列之(二 ...

  3. 使用SpringBoot访问jsp页面

    1 编写application.yml文件 spring: mvc: view: suffix: .jsp prefix: /jsp/ 2 创建Controller层 @Controller @Req ...

  4. lyft amundsen简单试用

    昨天有说过amundsen 官方为我们提供了dockerc-compose 运行的参考配置,以下是一个来自官方的 quick start clone amundsen 代码 amundsen 使用了g ...

  5. vue关于keep-alive的小坑

    在移动端里 少不了底部导航 在做底部导航的时候点击都会重复请求 我就使用了keep-alive来缓存 每次点击的时候走缓存 这里还有个用途就是当有列表的时候点进详情在返回可以保存之前的滚动记录 不会刷 ...

  6. c博客作业01--顺序分支结构

    0.展示PTA总分 1.本章学习总结 1.1 学习内容总结 1.运算符需注意的要点 '/'的左右两边如果均为整型数,其结果也为整型:'%'的左右两边只能为整型数: 优先级:逻辑运算符<关系运算符 ...

  7. DACL原理.控制文件的访问权限(文件,注册表.目录.等任何带有安全属性的对象.)

    目录 一丶简介 1.DACL是什么. 2.如何创建一个自己控制的文件. 3.SDDL是个什么鬼. 二丶 编写SDDL 控制的文件 一丶简介 1.DACL是什么. DACL称为自主访问的控制列表.是应用 ...

  8. vue中父级与子组件生命周期的先后顺序

    1.vue的生命周期 2.views/createrCustormer.vue为父级     <template>     <expressService />   </ ...

  9. 2019牛客国庆集训派对day1

    C 存每个值存在的位置,枚举末尾的值,再枚举前面的值,哈希二分出最长相同的,即剩下的为不同的 D \(f_{i,j,k}\)为前i位,最后一个3因子在j,次因子在k G bitset处理有多少位置符合 ...

  10. fluent运行过程中转换边界

    我们以一个简单的VOF算例来说明,算例模型如下: 算例中空气为主相,水为次相.开始时刻,inlet_one设置为速度入口边界,速度为1m/s,且水的体积分数为100%,inlet_two设置为速度入口 ...