[Swift]LeetCode127. 单词接龙 | Word Ladder
★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
➤微信公众号:山青咏芝(shanqingyongzhi)
➤博客园地址:山青咏芝(https://www.cnblogs.com/strengthen/)
➤GitHub地址:https://github.com/strengthen/LeetCode
➤原文地址:https://www.cnblogs.com/strengthen/p/9957561.html
➤如果链接不是山青咏芝的博客园地址,则可能是爬取作者的文章。
➤原文已修改更新!强烈建议点击原文地址阅读!支持作者!支持原创!
★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
Given two words (beginWord and endWord), and a dictionary's word list, find the length of shortest transformation sequence from beginWord to endWord, such that:
- Only one letter can be changed at a time.
- Each transformed word must exist in the word list. Note that beginWord is not a transformed word.
Note:
- Return 0 if there is no such transformation sequence.
- All words have the same length.
- All words contain only lowercase alphabetic characters.
- You may assume no duplicates in the word list.
- You may assume beginWord and endWord are non-empty and are not the same.
Example 1:
Input: beginWord = "hit", endWord = "cog", wordList = ["hot","dot","dog","lot","log","cog"] Output: 5 Explanation: As one shortest transformation is "hit" -> "hot" -> "dot" -> "dog" -> "cog", return its length 5.
Example 2:
Input: beginWord = "hit" endWord = "cog" wordList = ["hot","dot","dog","lot","log"] Output: 0 Explanation: The endWord "cog" is not in wordList, therefore no possible transformation.
给定两个单词(beginWord 和 endWord)和一个字典,找到从 beginWord 到 endWord 的最短转换序列的长度。转换需遵循如下规则:
- 每次转换只能改变一个字母。
- 转换过程中的中间单词必须是字典中的单词。
说明:
- 如果不存在这样的转换序列,返回 0。
- 所有单词具有相同的长度。
- 所有单词只由小写字母组成。
- 字典中不存在重复的单词。
- 你可以假设 beginWord 和 endWord 是非空的,且二者不相同。
示例 1:
输入: beginWord = "hit", endWord = "cog", wordList = ["hot","dot","dog","lot","log","cog"] 输出: 5 解释: 一个最短转换序列是 "hit" -> "hot" -> "dot" -> "dog" -> "cog", 返回它的长度 5。
示例 2:
输入: beginWord = "hit" endWord = "cog" wordList = ["hot","dot","dog","lot","log"] 输出: 0 解释: endWord "cog" 不在字典中,所以无法进行转换。
304ms
1 class Solution { 2 func ladderLength(_ beginWord: String, _ endWord: String, _ wordList: [String]) -> Int { 3 if !wordList.contains(endWord) 4 { 5 return 0 6 } 7 8 let dict = Set(wordList) 9 var beginSet = Set<String>() 10 var endSet = Set<String>() 11 var visitedSet = Set<String>() 12 var length = 1 13 var found = false 14 15 beginSet.insert(beginWord) 16 endSet.insert(endWord) 17 18 while !found && !beginSet.isEmpty && !endSet.isEmpty { 19 var nextSet = Set<String>() 20 //accelerating search speed by swap begin and end 21 if beginSet.count > endSet.count { 22 swap(&beginSet, &endSet) 23 } 24 found = helper(beginSet, endSet, dict, &visitedSet, &nextSet) 25 beginSet = nextSet 26 length += 1 27 } 28 return found ? length : 0 29 } 30 31 private func helper(_ beginSet: Set<String>, _ endSet: Set<String>, _ dict: Set<String>, 32 _ visitedSet: inout Set<String>, _ resSet: inout Set<String>) -> Bool { 33 34 let alphaArray = Array("abcdefghijklmnopqrstuvwxyz") 35 36 for word in beginSet { 37 for i in 0 ..< word.count { 38 var chars = Array(word) 39 for j in 0 ..< alphaArray.count{ 40 chars[i] = alphaArray[j] 41 let str = String(chars) 42 if dict.contains(str) { 43 if endSet.contains(str) 44 { 45 return true 46 } 47 if !visitedSet.contains(str) 48 { 49 resSet.insert(str) 50 visitedSet.insert(str) 51 } 52 } 53 } 54 } 55 } 56 return false 57 } 58 }
884ms
1 class Solution { 2 func ladderLength(_ beginWord: String, _ endWord: String, _ wordList: [String]) -> Int { 3 // ensure the endWord is in the wordList 4 if !wordList.contains(endWord) { 5 return 0 6 } 7 8 // build a word map with char array representations 9 // this saves an enormous amount of time 10 var wordMap: [String: [Character]] = [:] 11 for i in 0..<wordList.count { 12 // beginWord should never be in the list 13 if wordList[i] == beginWord { 14 continue 15 } 16 wordMap[wordList[i]] = [Character](wordList[i].characters) 17 } 18 19 // we work the problem from the begining forward and end backwards 20 // this allows us to deal with the smallest set of possibilities in 21 // both "directions" 22 var beginMap:[String: [Character]] = [beginWord: [Character](beginWord.characters)] 23 var endMap:[String: [Character]] = [endWord: [Character](endWord.characters)] 24 25 // burnedMap bridges what has been used in both beginMap or endMap 26 var burnedMap:[String: [Character]] = [:] 27 28 // word len is stable throughout the iterations, pay for it only once 29 let wordLen = beginWord.count 30 31 // mutations tracts how many time mutated on the road from beginWord to endWord 32 var mutations = 1 33 34 // we continue while beginMap and endMap are non-empty 35 // if one empties, there is no path from beginWord to endWord 36 while !beginMap.isEmpty && !endMap.isEmpty { 37 // minimize the working set by swapping in the smaller 38 // set, we only care about the mutation count 39 if beginMap.count > endMap.count { 40 let tempMap = beginMap 41 beginMap = endMap 42 endMap = tempMap 43 } 44 45 // track the upcoming set of mutation candidates we're about to create 46 var newMap: [String: [Character]] = [:] 47 48 // iterate throught he last set of mutation candidates 49 for candidateKV in beginMap { 50 let candidateChars = candidateKV.value 51 52 // walk through all the remaining (un-burned) valid words 53 for wordKV in wordMap { 54 let word = wordKV.key 55 let wordChars = wordKV.value 56 57 // diff the candidate and valid words looking 58 // fir diffs of 1 character 59 var diffs = 0 60 61 for i in 0..<wordLen { 62 if candidateChars[i] != wordChars[i] { 63 diffs += 1 64 if diffs > 1 { 65 break 66 } 67 } 68 } 69 70 // anything other than a diff of 1 is not applicable 71 // in this loop, we can only consider "word" if the 72 // diff is exactly 1 73 if diffs == 1 { 74 // we have a valid 1 character diff, use it 75 76 if endMap[word] != nil { 77 // base case reached 78 return mutations + 1 79 } 80 81 if burnedMap[word] == nil { 82 newMap[word] = wordChars 83 burnedMap[word] = wordChars 84 } 85 } 86 } 87 } 88 89 // we already evaluated what was in beginMap, we can 90 // lose those and use the newMap evaluation set 91 beginMap = newMap 92 93 // we have completed an additional mutation step 94 mutations += 1 95 } 96 97 return 0 98 } 99 }
1520ms
1 class Solution { 2 func ladderLength(_ beginWord: String, _ endWord: String, _ wordList: [String]) -> Int { 3 //Set类型替换数组类型,加速查找 4 var wordSet:Set<String> = Set<String>(wordList) 5 //构建队列,实现广度优先遍历 6 var q:Queue<String> = Queue<String>() 7 //加入源顶点 8 q.enQueue(beginWord) 9 var res:Int = 0 10 while (!q.isEmpty()) 11 { 12 for k in (1...q.count).reversed() 13 { 14 var word:String = q.deQueue()! 15 if word == endWord {return res + 1} 16 for i in 0..<word.count 17 { 18 var newWord:String = word 19 for ch in 97...122 20 { 21 newWord[i] = ch.ASCII 22 if wordSet.contains(newWord) && newWord != word 23 { 24 q.enQueue(newWord) 25 wordSet.remove(newWord) 26 } 27 } 28 } 29 } 30 res += 1 31 } 32 return 0 33 } 34 } 35 36 //String扩展方法 37 extension String { 38 func toCharArray() -> [Character] 39 { 40 var arr:[Character] = [Character]() 41 for char in self.characters 42 { 43 arr.append(char) 44 } 45 return arr 46 } 47 48 //subscript函数可以检索数组中的值 49 //直接按照索引方式截取指定索引的字符 50 subscript (_ i: Int) -> Character { 51 //读取字符 52 get {return self[index(startIndex, offsetBy: i)]} 53 54 //修改字符 55 set 56 { 57 var str:String = self 58 var index = str.index(startIndex, offsetBy: i) 59 str.remove(at: index) 60 str.insert(newValue, at: index) 61 self = str 62 } 63 } 64 } 65 66 //Int扩展方法 67 extension Int 68 { 69 //属性:ASCII值(定义大写为字符值) 70 var ASCII:Character 71 { 72 get {return Character(UnicodeScalar(self)!)} 73 } 74 } 75 76 public struct Queue<T> { 77 78 // 泛型数组:用于存储数据元素 79 fileprivate var queue: [T] 80 81 // 返回队列中元素的个数 82 public var count: Int { 83 return queue.count 84 } 85 86 // 构造函数:创建一个空的队列 87 public init() { 88 queue = [T]() 89 } 90 91 //通过既定数组构造队列 92 init(_ arr:[T]){ 93 queue = arr 94 } 95 96 // 检查队列是否为空 97 // - returns: 如果队列为空,则返回true,否则返回false 98 public func isEmpty() -> Bool { 99 return queue.isEmpty 100 } 101 102 // 入队列操作:将元素添加到队列的末尾 103 public mutating func enQueue(_ element: T) { 104 queue.append(element) 105 } 106 107 // 出队列操作:删除并返回队列中的第一个元素 108 public mutating func deQueue() -> T? { 109 return queue.removeFirst() 110 } 111 }
1692ms
1 class Solution { 2 func ladderLength(_ beginWord: String, _ endWord: String, _ wordList: [String]) -> Int { 3 4 var wordSet = Set(wordList) 5 var level = 0 6 var currentLevelWords = [beginWord] 7 8 while !currentLevelWords.isEmpty { 9 level += 1 10 11 var nextLevelWords: [String] = [] 12 13 for word in currentLevelWords { 14 if word == endWord { 15 return level 16 } 17 18 nextLevelWords += findRelatedWords(word: word, wordSet: &wordSet) 19 } 20 21 currentLevelWords = nextLevelWords 22 } 23 24 return 0 25 } 26 27 private let alphabet = Array("abcdefghijklmnopqrstuvwxyz") 28 29 func findRelatedWords(word: String, wordSet: inout Set<String>) -> [String] { 30 var relatedWords: [String] = [] 31 32 var word = word 33 var wordIndex = word.startIndex 34 while wordIndex < word.endIndex { 35 36 let originalCharacter = word[wordIndex] 37 38 for character in alphabet { 39 word.remove(at: wordIndex) 40 word.insert(character, at: wordIndex) 41 42 if wordSet.contains(word) { 43 wordSet.remove(word) 44 45 relatedWords.append(word) 46 } 47 } 48 49 word.remove(at: wordIndex) 50 word.insert(originalCharacter, at: wordIndex) 51 52 wordIndex = word.index(after: wordIndex) 53 } 54 55 return relatedWords 56 } 57 }
1788ms
1 class Solution { 2 func ladderLength(_ beginWord: String, _ endWord: String, _ wordList: [String]) -> Int { 3 4 var wordSet = Set(wordList) 5 var currentLevelWords = [beginWord] 6 var level = 0 7 8 while !currentLevelWords.isEmpty { 9 level += 1 10 11 var nextLevelWords: [String] = [] 12 13 for word in currentLevelWords { 14 15 if word == endWord { 16 return level 17 } 18 19 nextLevelWords += findCloseWords(word: word, wordSet: &wordSet) 20 } 21 22 currentLevelWords = nextLevelWords 23 } 24 25 return 0 26 } 27 28 let alphabet = Array("abcdefghijklmnopqrstuvwxyz") 29 30 func findCloseWords(word: String, wordSet: inout Set<String>) -> [String] { 31 32 var closeWords: [String] = [] 33 34 var word = word 35 var wordIndex = word.startIndex 36 37 while wordIndex < word.endIndex { 38 39 let originalCharacter = word[wordIndex] 40 41 for character in alphabet { 42 word.remove(at: wordIndex) 43 word.insert(character, at: wordIndex) 44 45 if wordSet.contains(word) { 46 closeWords.append(word) 47 wordSet.remove(word) 48 } 49 } 50 51 word.remove(at: wordIndex) 52 word.insert(originalCharacter, at: wordIndex) 53 54 wordIndex = word.index(after: wordIndex) 55 } 56 57 return closeWords 58 } 59 }
2284ms
1 class Solution { 2 func ladderLength(_ beginWord: String, _ endWord: String, _ wordList: [String]) -> Int { 3 var queue = [beginWord] 4 var wordList = Set(wordList) 5 var length = 1 6 7 while !queue.isEmpty && !wordList.isEmpty { 8 let count = queue.count 9 for _ in 0..<count { 10 let word = queue.removeFirst() 11 12 let validTransforms = validTransformations(word, wordList) 13 for nextWord in validTransforms { 14 if nextWord == endWord { 15 return length + 1 16 } 17 wordList.remove(nextWord) 18 queue.append(nextWord) 19 } 20 } 21 22 length += 1 23 } 24 25 return 0 26 } 27 28 func validTransformations(_ word: String, _ wordList: Set<String>) -> [String] { 29 var result = [String]() 30 let alpahabetArray = Array("abcdefghijklmnopqrstuvwxyz".characters) 31 for i in 0 ..< word.characters.count { 32 var chars = Array(word.characters) 33 for j in 0 ..< alpahabetArray.count { 34 chars[i] = alpahabetArray[j] 35 let newWord = String(chars) 36 if wordList.contains(newWord) { 37 result.append(newWord) 38 } 39 } 40 } 41 42 return result 43 } 44 }
3624ms
1 class Solution { 2 3 var letters = "abcdefghijklmnopqrstuvwxyz" 4 func ladderLength(_ beginWord: String, _ endWord: String, _ wordList: [String]) -> Int { 5 // var map: [String:Set<String>] = [:] 6 7 8 var wordSet = Set<String>(wordList) 9 // wordSet.insert(endWord) 10 var queue: [[String]] = [[beginWord]] 11 var visited: Set<String> = [] 12 var ans = 1 13 while !queue.isEmpty { 14 var words = queue.removeLast() 15 var construction: [String] = [] 16 for curr in words { 17 // print(curr) 18 if !visited.contains(curr) { 19 if curr == endWord {return ans} 20 var wordsToAdd = getWords(curr, wordSet) 21 // var wordsToAdd = Array(map[curr] ?? []) 22 construction.append(contentsOf: wordsToAdd) 23 visited.insert(curr) 24 } 25 } 26 if !construction.isEmpty {queue.insert(construction, at: 0)} 27 ans += 1 28 } 29 return 0 30 } 31 32 func getWords(_ str1: String, _ validWords: Set<String>) -> [String] { 33 var ans : [String] = [] 34 var strArr = Array(str1) 35 var lettersArr = Array(letters) 36 for i in 0..<str1.count { 37 for j in 0..<lettersArr.count { 38 var temp = strArr[i] 39 strArr[i] = lettersArr[j] 40 var newStr = String(strArr) 41 if validWords.contains(newStr) { 42 ans.append(String(strArr)) 43 } 44 strArr[i] = temp 45 } 46 } 47 return ans 48 } 49 50 func isOneAway(_ str1: String, _ str2: String) -> Bool { 51 var arr1 = Array(str1), arr2 = Array(str2) 52 for i in 0..<arr1.count { 53 if arr1[i] != arr2[i] { 54 arr1[i] = arr2[i] 55 return arr1 == arr2 56 } 57 } 58 return false 59 } 60 }
3984ms
1 class Solution { 2 func ladderLength(_ beginWord: String, _ endWord: String, _ wordList: [String]) -> Int { 3 guard wordList.contains(endWord) else { 4 return 0 5 } 6 7 var wordMap: [String: Int] = [:] 8 for word in wordList { 9 wordMap[word] = 1 10 } 11 12 var sequence: [String] = [beginWord] 13 var map: [String: Int] = [beginWord : 1] 14 let letters = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", 15 "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"] 16 17 while sequence.count > 0 { 18 19 let bw = sequence.removeFirst() 20 21 let level = map[bw]! 22 23 let bws: [Character] = [Character](bw) 24 for i in 0..<bws.count { 25 var chas = bws 26 for c in letters { 27 if bws[i] != Character(c) { 28 chas[i] = Character(c) 29 let word = String(chas) 30 31 if word == endWord { 32 return level + 1 33 } 34 35 if wordMap[word] != nil && map[word] == nil { 36 map[word] = level+1 37 sequence.append(word) 38 } 39 } 40 } 41 } 42 } 43 44 return 0 45 } 46 }