[Swift]LeetCode752. 打開轉盤鎖 | Open the Lock

★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
➤微信公衆號:山青詠芝(shanqingyongzhi)
➤博客園地址:山青詠芝(https://www.cnblogs.com/strengthen/
➤GitHub地址:https://github.com/strengthen/LeetCode
➤原文地址: http://www.javashuo.com/article/p-sqefldiy-me.html 
➤若是連接不是山青詠芝的博客園地址,則多是爬取做者的文章。
➤原文已修改更新!強烈建議點擊原文地址閱讀!支持做者!支持原創!
★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★html

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

The lock initially starts at '0000', a string representing the state of the 4 wheels.git

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

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:app

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:ide

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

Example 3:ui

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:spa

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

Note:code

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

你有一個帶有四個圓形撥輪的轉盤鎖。每一個撥輪都有10個數字: '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' 。每一個撥輪能夠自由旋轉:例如把 '9' 變爲  '0''0' 變爲 '9' 。每次旋轉都只能旋轉一個撥輪的一位數字。

鎖的初始數字爲 '0000' ,一個表明四個撥輪的數字的字符串。

列表 deadends 包含了一組死亡數字,一旦撥輪的數字和列表裏的任何一個元素相同,這個鎖將會被永久鎖定,沒法再被旋轉。

字符串 target 表明能夠解鎖的數字,你須要給出最小的旋轉次數,若是不管如何不能解鎖,返回 -1。 

示例 1:

輸入:deadends = ["0201","0101","0102","1212","2002"], 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","8889","8878","8898","8788","8988","7888","9888"], target = "8888"
輸出:-1
解釋:
沒法旋轉到目標數字且不被鎖定。

示例 4:

輸入: deadends = ["0000"], target = "8888"
輸出:-1 

提示:

  1. 死亡列表 deadends 的長度範圍爲 [1, 500]
  2. 目標數字 target 不會在 deadends之中。
  3. 每一個 deadends 和 target 中的字符串的數字會在 10,000 個可能的狀況 '0000' 到 '9999' 中產生。

Runtime: 296 ms
Memory Usage: 20.1 MB
 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(), v2: v2, v3: v3, v4: v4),
 25                 Value(v1: v1, v2: v2.incrementDecimal(), v3: v3, v4: v4),
 26                 Value(v1: v1, v2: v2.decrementDecimal(), v3: v3, v4: v4),
 27                 Value(v1: v1, v2: v2, v3: v3.incrementDecimal(), v4: v4),
 28                 Value(v1: v1, v2: v2, v3: v3.decrementDecimal(), v4: v4),
 29                 Value(v1: v1, v2: v2, v3: v3, v4: v4.incrementDecimal()),
 30                 Value(v1: v1, v2: v2, v3: v3, 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, 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,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,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], _ 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,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                 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 }

Runtime: 864 ms
Memory Usage: 20.3 MB
 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 }
相關文章
相關標籤/搜索