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:
- 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‘
.
你有一个带有四个圆形拨轮的转盘锁。每个拨轮都有10个数字: ‘0‘,‘9‘
。每个拨轮可以自由旋转:例如把 ‘9‘
变为 ‘0‘
,‘0‘
变为 ‘9‘
。每次旋转都只能旋转一个拨轮的一位数字。
锁的初始数字为 ‘0000‘
,一个代表四个拨轮的数字的字符串。
列表 deadends
包含了一组死亡数字,一旦拨轮的数字和列表里的任何一个元素相同,这个锁将会被永久锁定,无法再被旋转。
字符串 target
代表可以解锁的数字,你需要给出最小的旋转次数,如果无论如何不能解锁,返回 -1。
示例 1:
输入:deadends = ["0201",target = "0202" 输出:6 解释: 可能的移动序列为 "0000" -> "1000" -> "1100" -> "1200" -> "1201" -> "1202" -> "0202"。 注意 "0000" -> "0001" -> "0002" -> "0102" -> "0202" 这样的序列是不能解锁的, 因为当拨动到 "0102" 时这个锁就会被锁定。
示例 2:
输入: deadends = ["8888"],target = "0009" 输出:1 解释: 把最后一位反向旋转一次即可 "0000" -> "0009"。
示例 3:
输入: deadends = ["8887",target = "8888" 输出:-1 解释: 无法旋转到目标数字且不被锁定。
示例 4:
输入: deadends = ["0000"],target = "8888" 输出:-1
提示:
- 死亡列表
deadends
的长度范围为[1,500]
。 - 目标数字
target
不会在deadends
之中。 - 每个
deadends
和target
中的字符串的数字会在 10,000 个可能的情况‘0000‘
到‘9999‘
中产生。
1 class Solution { 2 func openLock(_ deadends: [String],_ target: String) -> Int { 3 var start: Set<String> = [] 4 var end: Set<String> = [] 5 let deadends: Set<String> = Set(deadends) 6 7 var startToNow: Set<String> = [] 8 var endToNow: Set<String> = [] 9 10 start.insert("0000") 11 end.insert(target) 12 13 startToNow.insert("0000") 14 endToNow.insert(target) 15 16 var steps = 0 17 18 // Tip: Start from both sides 19 while !start.isEmpty && !end.isEmpty { 20 var temp: Set<String> = [] 21 22 if !start.intersection(end).isEmpty { 23 return steps 24 } 25 26 for s in start { 27 if deadends.contains(s) { continue } 28 29 for next in getNext(s) { 30 if !deadends.contains(next) && !startToNow.contains(next) { 31 temp.insert(next) 32 } 33 } 34 } 35 steps += 1 36 37 (start,end) = (end,temp) 38 (startToNow,endToNow) = (endToNow,startToNow.union(temp)) 39 } 40 41 return -1 42 } 43 44 private func getNext(_ s: String) -> Set<String> { 45 var s: [Int] = Array(s).map{ return Int(String($0))! } 46 var res: Set<String> = [] 47 48 for i in 0 ..< 4 { 49 var t = s 50 t[i] = (s[i] + 1) % 10 51 res.insert(t.reduce("",{ return $0 + String($1) })) 52 t[i] = (s[i] - 1 + 10) % 10 53 res.insert(t.reduce("",{ return $0 + String($1) })) 54 } 55 return res 56 } 57 }
560ms
1 class Solution { 2 struct Value: Hashable,customstringconvertible { 3 let v1: Int 4 let v2: Int 5 let v3: Int 6 let v4: Int 7 8 init(v1: Int,v2: Int,v3: Int,v4: Int) { 9 self.v1 = v1 10 self.v2 = v2 11 self.v3 = v3 12 self.v4 = v4 13 } 14 15 init(value: Int) { 16 let digits = Value.convertToDigits(number: value) 17 18 self.init(v1: digits[0],v2: digits[1],v3: digits[2],v4: digits[3]) 19 } 20 21 var nextValues: [Value] { 22 return [ 23 Value(v1: v1.incrementDecimal(),v2: v2,v3: v3,v4: v4), 24 Value(v1: v1.decrementDecimal(), 25 Value(v1: v1,v2: v2.incrementDecimal(), 26 Value(v1: v1,v2: v2.decrementDecimal(), 27 Value(v1: v1,v3: v3.incrementDecimal(), 28 Value(v1: v1,v3: v3.decrementDecimal(), 29 Value(v1: v1,v4: v4.incrementDecimal()), 30 Value(v1: v1,v4: v4.decrementDecimal()), 31 ] 32 } 33 34 var description: String { 35 return "\(v1)\(v2)\(v3)\(v4)" 36 } 37 38 private static func convertToDigits(number: Int) -> [Int] { 39 let first = number % 10 40 let second = (number % 100) / 10 41 let third = (number % 1000) / 100 42 let fourth = (number % 10000) / 1000 43 44 return [fourth,third,second,first] 45 } 46 } 47 48 class Combination: Hashable { 49 let value: Value 50 let step: Int 51 52 init(value: Value,step: Int) { 53 self.value = value 54 self.step = step 55 } 56 57 var hashValue: Int { 58 return value.hashValue 59 } 60 61 static func == (lhs: Solution.Combination,rhs: Solution.Combination) -> Bool { 62 return lhs.value == rhs.value 63 } 64 } 65 66 private var queue: [Combination] = [] 67 private var queuedValues: Set<Value> = [] 68 69 func openLock(_ deadends: [String],_ target: String) -> Int { 70 let convertedDeadends = deadends.map { Int($0)! } 71 let convertedTarget = Int(target)! 72 73 return openLock(convertedDeadends,convertedTarget) 74 } 75 76 private func openLock(_ deadends: [Int],_ target: Int) -> Int { 77 let initialCombination = Combination(value: Value(value: 0),step: 0) 78 let targetValue = Value(value: target) 79 let deadendValues = Set(deadends.map { Value(value: $0) }) 80 81 queue.append(initialCombination) 82 83 while !queue.isEmpty { 84 let combination = queue.removeFirst() 85 86 guard !deadendValues.contains(combination.value) else { continue } 87 if combination.value == targetValue { 88 return combination.step 89 } 90 91 enqueuePossibleCombinations(from: combination) 92 } 93 94 return -1 95 } 96 97 private func enqueuePossibleCombinations(from combination: Combination) { 98 let nextStep = combination.step + 1 99 100 combination.value.nextValues.forEach { 101 enqueueIfPossible(value: $0,step: nextStep) 102 } 103 } 104 105 private func enqueueIfPossible(value: Value,step: Int) { 106 guard !queuedValues.contains(value) else { return } 107 queuedValues.insert(value) 108 109 let combination = Combination(value: value,step: step) 110 queue.append(combination) 111 } 112 } 113 114 extension Int { 115 func incrementDecimal() -> Int { 116 return (self + 1) % 10 117 } 118 119 func decrementDecimal() -> Int { 120 return (self - 1 + 10) % 10 121 } 122 }
696ms
1 final class Solution { 2 3 func openLock(_ deadends: [String],_ target: String) -> Int { 4 let deadends = Set(deadends.map { str in 5 Array(str).map { Int(String($0))! } 6 }) 7 let target = Array(target).map { Int(String($0))! } 8 let start = [0,0,0] 9 var q = Queue<[Int]>() 10 var visited = Set<[Int]>() 11 q.push(start) 12 visited.insert(start) 13 var count = 0 14 while !q.isEmpty { 15 let size = q.size 16 for i in 0..<q.size { 17 let node = q.pop() 18 if deadends.contains(node) { continue } 19 if target == node { return count } 20 for (i,digit) in node.enumerated() { 21 var neighbor = node 22 neighbor[i] = (digit + 1) % 10 23 if !visited.contains(neighbor) { 24 q.push(neighbor) 25 visited.insert(neighbor) 26 } 27 neighbor = node 28 neighbor[i] = (digit - 1) 29 if neighbor[i] == -1 { neighbor[i] = 9 } 30 if !visited.contains(neighbor) { 31 q.push(neighbor) 32 visited.insert(neighbor) 33 } 34 } 35 } 36 count += 1 37 } 38 return -1 39 } 40 } 41 42 struct Queue<T> { 43 var arr = [T]() 44 var head = 0 45 46 mutating func push(_ val: T) { 47 arr.append(val) 48 } 49 50 mutating func pop() -> T { 51 let res = arr[head] 52 head += 1 53 return res 54 } 55 56 var isEmpty: Bool { 57 return head >= arr.count 58 } 59 60 var size: Int { 61 return arr.count - head 62 } 63 }
704ms
1 typealias Combo = [Int] 2 3 class Solution { 4 func openLock(_ deadends: [String],_ target: String) -> Int { 5 let target = Array(target).compactMap {Int(String($0)) } 6 let start = [0,0] 7 var seen = Set<Combo>() 8 9 for word in deadends { 10 seen.insert(Array(word).map { Int(String($0))! }) 11 } 12 guard !seen.contains(start) else { return -1 } 13 var q: [Combo] = [start] 14 var level = 0 15 16 while !q.isEmpty { 17 18 var tempQ: [Combo] = [] 19 for current in q { 20 guard current != target else { return level } 21 seen.insert(current) // add to deadends/visited list 22 23 for i in 0 ..< current.count { 24 var nextValue = current 25 nextValue[i].next() 26 27 if !seen.contains(nextValue) { 28 tempQ.append(nextValue) 29 seen.insert(nextValue) 30 } 31 32 var prevValue = current 33 prevValue[i].prev() 34 35 if !seen.contains(prevValue) { 36 tempQ.append(prevValue) 37 seen.insert(prevValue) 38 } 39 } 40 } 41 level += 1 42 q = tempQ 43 44 } 45 return -1 46 } 47 } 48 49 extension Int { 50 mutating func next() { 51 self = (self + 1) % 10 52 } 53 mutating func prev() { 54 self = (self + 10 - 1) % 10 55 } 56 }
772ms
1 class Solution { 2 func openLock(_ deadends: [String],_ target: String) -> Int { 3 let target = Array(target).map{ Int("\($0)")! } 4 let deadends = Set(Array(deadends).map{ $0.map{ Int("\($0)")! } }) 5 var visited = Set<[Int]>() 6 var queue = [([0,0],0)] 7 var i = 0 8 while i != queue.count { 9 let (candidate,index) = queue[i] 10 i += 1 11 if visited.contains(candidate) || deadends.contains(candidate) { continue } 12 if candidate == target { return index } 13 visited.insert(candidate) 14 for j in 0...3 { 15 var c = candidate 16 c[j] = (c[j] + 1) % 9 17 queue.append((c,index+1)) 18 } 19 for j in 0...3 { 20 var c = candidate 21 c[j] = c[j] == 0 ? 9 : c[j] - 1 22 queue.append((c,index+1)) 23 } 24 if i > 100000 { return -10000} 25 } 26 return -1 27 } 28 }
784ms
1 class Solution { 2 func openLock(_ deadends: [String],index) = queue[i] 10 i += 1 11 if visited.contains(candidate) || deadends.contains(candidate) { continue } 12 if candidate == target { return index } 13 visited.insert(candidate) 14 for j in 0...3 { 15 var c = candidate 16 var cj = c[j] 17 c[j] = (cj + 1) % 9 18 queue.append((c,index+1)) 19 c[j] = cj == 0 ? 9 : cj - 1 20 queue.append((c,index+1)) 21 } 22 } 23 return -1 24 } 25 }
1 class Solution { 2 func openLock(_ deadends: [String],_ target: String) -> Int { 3 var deadlock:Set<String> = Set<String>(deadends) 4 if deadlock.contains("0000") 5 { 6 return -1 7 } 8 var res:Int = 0 9 var visited:Set<String> = ["0000"] 10 var q:[String] = ["0000"] 11 while(!q.isEmpty) 12 { 13 res += 1 14 for k in stride(from:q.count,to:0,by:-1) 15 { 16 let t:String = q.removeFirst() 17 let arrChar:[Character] = Array(t) 18 let arrInt:[Int] = arrChar.map{$0.ascii} 19 for i in 0..<t.count 20 { 21 var c:Character = arrChar[i] 22 var num:Int = arrInt[i] 23 var str1:String = t.subString(0,i) + String(c == "9" ? 0 : num - 48 + 1) + t.subString(i + 1) 24 var str2:String = t.subString(0,i) + String(c == "0" ? 9 : num - 48 - 1) + t.subString(i + 1) 25 if str1 == target || str2 == target 26 { 27 return res 28 } 29 if !visited.contains(str1) && !deadlock.contains(str1) 30 { 31 q.append(str1) 32 } 33 if !visited.contains(str2) && !deadlock.contains(str2) 34 { 35 q.append(str2) 36 } 37 visited.insert(str1) 38 visited.insert(str2) 39 } 40 } 41 } 42 return -1 43 } 44 } 45 46 //Character扩展 47 extension Character 48 { 49 //Character转ASCII整数值(定义小写为整数值) 50 var ascii: Int { 51 get { 52 return Int(self.unicodeScalars.first?.value ?? 0) 53 } 54 } 55 } 56 57 extension String { 58 // 截取字符串:指定索引和字符数 59 // - begin: 开始截取处索引 60 // - count: 截取的字符数量 61 func subString(_ begin:Int,_ count:Int) -> String { 62 let start = self.index(self.startIndex,offsetBy: max(0,begin)) 63 let end = self.index(self.startIndex,offsetBy: min(self.count,begin + count)) 64 return String(self[start..<end]) 65 } 66 67 // 截取字符串:从index到结束处 68 // - Parameter index: 开始索引 69 // - Returns: 子字符串 70 func subString(_ index: Int) -> String { 71 let theIndex = self.index(self.endindex,offsetBy: index - self.count) 72 return String(self[theIndex..<endindex]) 73 } 74 }