[LeetCode] 127. Word Ladder
A transformation sequence from word beginWord
to word endWord
using a dictionary wordList
is a sequence of words beginWord -> s1 -> s2 -> ... -> sk
such that:
- Every adjacent pair of words differs by a single letter.
- Every
si
for1 <= i <= k
is inwordList
. Note thatbeginWord
does not need to be inwordList
. sk == endWord
Given two words, beginWord
and endWord
, and a dictionary wordList
, return the number of words in the shortest transformation sequence from beginWord
to endWord
, or 0
if no such sequence exists.
Example 1:
Input: beginWord = "hit", endWord = "cog", wordList = ["hot","dot","dog","lot","log","cog"] Output: 5 Explanation: One shortest transformation sequence is "hit" -> "hot" -> "dot" -> "dog" -> cog", which is 5 words long.
Example 2:
Input: beginWord = "hit", endWord = "cog", wordList = ["hot","dot","dog","lot","log"] Output: 0 Explanation: The endWord "cog" is not in wordList, therefore there is no valid transformation sequence.
Constraints:
1 <= beginWord.length <= 10
endWord.length == beginWord.length
1 <= wordList.length <= 5000
wordList[i].length == beginWord.length
beginWord
,endWord
, andwordList[i]
consist of lowercase English letters.beginWord != endWord
- All the words in
wordList
are unique.
单词接龙。
字典 wordList 中从单词 beginWord 和 endWord 的 转换序列 是一个按下述规格形成的序列 beginWord -> s1 -> s2 -> ... -> sk:
每一对相邻的单词只差一个字母。
对于 1 <= i <= k 时,每个 si 都在 wordList 中。注意, beginWord 不需要在 wordList 中。
sk == endWord
给你两个单词 beginWord 和 endWord 和一个字典 wordList ,返回 从 beginWord 到 endWord 的 最短转换序列 中的 单词数目 。如果不存在这样的转换序列,返回 0 。来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/word-ladder
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
这道题的思路是BFS,不难想到。通过这道题,我们需要学会一个BFS加速的办法,双向BFS。
首先是普通的BFS。我们将 beginWord 放入队列,然后修改 beginWord 里的每一个字母,看看下一个单词是否存在于 wordList,如果存在的话,这条路就能继续走下去。我们需要返回的是一条最短路径。
时间O(n)
空间O(n)
Java实现
1 class Solution { 2 public int ladderLength(String beginWord, String endWord, List<String> wordList) { 3 HashSet<String> set = new HashSet<>(wordList); 4 // corner case 5 if (set.size() == 0 || !set.contains(endWord)) { 6 return 0; 7 } 8 9 // normal case 10 set.remove(beginWord); 11 Queue<String> queue = new LinkedList<>(); 12 queue.offer(beginWord); 13 HashSet<String> visited = new HashSet<>(); 14 int step = 1; 15 while (!queue.isEmpty()) { 16 int size = queue.size(); 17 for (int i = 0; i < size; i++) { 18 String curWord = queue.poll(); 19 if (helper(curWord, endWord, queue, visited, set)) { 20 return step + 1; 21 } 22 } 23 step++; 24 } 25 return 0; 26 } 27 28 private boolean helper(String curWord, String endWord, Queue<String> queue, HashSet<String> visited, HashSet<String> set) { 29 char[] letter = curWord.toCharArray(); 30 for (int i = 0; i < endWord.length(); i++) { 31 char origin = letter[i]; 32 for (char j = 'a'; j <= 'z'; j++) { 33 if (j == origin) { 34 continue; 35 } 36 letter[i] = j; 37 String nextWord = String.valueOf(letter); 38 if (set.contains(nextWord)) { 39 if (nextWord.equals(endWord)) { 40 return true; 41 } 42 if (!visited.contains(nextWord)) { 43 queue.offer(nextWord); 44 visited.add(nextWord); 45 } 46 } 47 } 48 letter[i] = origin; 49 } 50 return false; 51 } 52 }
双向 BFS 的思路是我们需要对 beginWord 和 endWord 分别使用一个 hashset,并且对 beginWord 和 endWord 同时修改一个字母,看看下一个单词是否存在于 wordList。这里面还有一个技巧,假设 beginWord 和 endWord 各自衍生出了一个 nextList,我们只需要再次遍历那个更短的 nextList 以节省时间。双向 BFS 会比第一种 BFS 节省一半的时间。
时间O(n)
空间O(n)
Java实现
1 class Solution { 2 public int ladderLength(String beginWord, String endWord, List<String> wordList) { 3 HashSet<String> set = new HashSet<>(wordList); 4 // corner case 5 if (set.size() == 0 || !set.contains(endWord)) { 6 return 0; 7 } 8 9 // normal case 10 HashSet<String> visited = new HashSet<>(); 11 HashSet<String> beginVisited = new HashSet<>(); 12 beginVisited.add(beginWord); 13 HashSet<String> endVisited = new HashSet<>(); 14 endVisited.add(endWord); 15 16 int step = 1; 17 while (!beginVisited.isEmpty() && !endVisited.isEmpty()) { 18 // 优先选择小的哈希表进行扩散,考虑到的情况更少 19 if (beginVisited.size() > endVisited.size()) { 20 HashSet<String> temp = beginVisited; 21 beginVisited = endVisited; 22 endVisited = temp; 23 } 24 25 HashSet<String> nextLevelVisited = new HashSet<>(); 26 for (String word : beginVisited) { 27 if (helper(word, endVisited, visited, set, nextLevelVisited)) { 28 return step + 1; 29 } 30 } 31 beginVisited = nextLevelVisited; 32 step++; 33 } 34 return 0; 35 } 36 37 private boolean helper(String word, HashSet<String> endVisited, 38 HashSet<String> visited, 39 HashSet<String> wordSet, 40 HashSet<String> nextLevelVisited) { 41 char[] letter = word.toCharArray(); 42 for (int i = 0; i < word.length(); i++) { 43 char origin = letter[i]; 44 for (char c = 'a'; c <= 'z'; c++) { 45 if (origin == c) { 46 continue; 47 } 48 letter[i] = c; 49 String nextWord = String.valueOf(letter); 50 if (wordSet.contains(nextWord)) { 51 if (endVisited.contains(nextWord)) { 52 return true; 53 } 54 if (!visited.contains(nextWord)) { 55 nextLevelVisited.add(nextWord); 56 visited.add(nextWord); 57 } 58 } 59 } 60 // 恢复,下次再用 61 letter[i] = origin; 62 } 63 return false; 64 } 65 }