Question
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 intermediate word must exist in the word list
For example,
Given:
beginWord = "hit"
endWord = "cog"
wordList = ["hot","dot","dog","lot","log"]
As one shortest transformation is "hit" -> "hot" -> "dot" -> "dog" -> "cog"
,
return its length 5
.
Note:
- Return 0 if there is no such transformation sequence.
- All words have the same length.
- All words contain only lowercase alphabetic characters.
Solution 1 -- BFS
1 class WordNode{ 2 String word; 3 int numSteps; 4 5 public WordNode(String word, int numSteps){ 6 this.word = word; 7 this.numSteps = numSteps; 8 } 9 } 10 11 public class Solution { 12 public int ladderLength(String beginWord, String endWord, Set<String> wordList) { 13 Queue<WordNode> queue = new LinkedList<WordNode>(); 14 queue.add(new WordNode(beginWord, 1)); 15 16 wordList.add(endWord); 17 18 // 由于这里纪录了每个词的最小步数,所以不用两个list 19 while (queue.size() > 0) { 20 WordNode topNode = queue.remove(); 21 String current = topNode.word; 22 23 if (current.equals(endWord)) 24 return topNode.numSteps; 25 26 char[] arr = current.toCharArray(); 27 // 穷举法 28 for (int i = 0; i < arr.length; i++) { 29 for (char c = 'a'; c <= 'z'; c++) { 30 char tmp = arr[i]; 31 if (arr[i] != c) 32 arr[i] = c; 33 34 String newWord = new String(arr); 35 if (wordList.contains(newWord)) { 36 queue.add(new WordNode(newWord, topNode.numSteps + 1)); 37 wordList.remove(newWord); 38 } 39 arr[i] = tmp; 40 } 41 } 42 } 43 return 0; 44 } 45 }
Solution 2 -- BFS & Adjacency List
Basic idea is: 1. construct adjacency list 2. BFS
Constructing adjacency list uses O(n2) time, and BFS is O(n). Total time complexity is O(n2), when testing in leetcode, it reports "time limit exceeded".
1 public class Solution { 2 public int ladderLength(String beginWord, String endWord, Set<String> wordList) { 3 if (wordList == null) 4 return -1; 5 int step = 1; 6 if (beginWord.equals(endWord)) 7 return 1; 8 // Construct adjacency lists for words 9 // Do not forget start word and end word 10 wordList.add(beginWord); 11 wordList.add(endWord); 12 Map<String, List<String>> adjacencyList = new HashMap<String, List<String>>(); 13 Map<String, Boolean> visited = new HashMap<String, Boolean>(); 14 int length = wordList.size(); 15 String[] wordList2 = new String[length]; 16 int i = 0; 17 for (String current : wordList) { 18 wordList2[i] = current; 19 i++; 20 } 21 // Initialization 22 for (i = 0; i < length; i++) { 23 adjacencyList.put(wordList2[i], new ArrayList<String>()); 24 visited.put(wordList2[i], false); 25 } 26 27 for (i = 0; i < length; i++) { 28 String current = wordList2[i]; 29 // Construct adjacency list for each element 30 for (int j = i + 1; j < length; j++) { 31 String next = wordList2[j]; 32 if (isAdjacent(current, next)) { 33 List<String> list1 = adjacencyList.get(current); 34 list1.add(next); 35 adjacencyList.put(current, list1); 36 List<String> list2 = adjacencyList.get(next); 37 list2.add(current); 38 adjacencyList.put(next, list2); 39 } 40 } 41 } 42 43 // BFS 44 List<String> current = new ArrayList<String>(); 45 List<String> next; 46 current.add(beginWord); 47 visited.put(beginWord, true); 48 while (current.size() > 0) { 49 step++; 50 next = new ArrayList<String>(); 51 for (String currentString : current) { 52 List<String> neighbors = adjacencyList.get(currentString); 53 if (neighbors == null) 54 continue; 55 for (String neighbor : neighbors) { 56 if (neighbor.equals(endWord)) 57 return step; 58 if (!visited.get(neighbor)) { 59 next.add(neighbor); 60 visited.put(neighbor, true); 61 } 62 } 63 } 64 current = next; 65 } 66 return -1; 67 68 } 69 70 private boolean isAdjacent(String current, String next) { 71 if (current.length() != next.length()) 72 return false; 73 int length = current.length(); 74 int diff = 0; 75 for (int i = 0; i < length; i++) { 76 char a = current.charAt(i); 77 char b = next.charAt(i); 78 if (a != b) 79 diff++; 80 } 81 if (diff == 1) 82 return true; 83 return false; 84 } 85 }
Python version
1 from collections import deque 2 from collections import defaultdict 3 4 class Solution: 5 def ladderLength(self, beginWord: str, endWord: str, wordList: List[str]) -> int: 6 if endWord not in wordList: 7 return 0 8 queue = deque() 9 queue.append((beginWord, 1)) 10 visited = set([beginWord]) 11 adjacency_map = defaultdict(list) 12 L = len(beginWord) 13 # Pre-process 14 for word in wordList: 15 for i in range(L): 16 adjacency_map[word[:i] + "*" + word[i+1:]].append(word) 17 # BFS 18 while queue: 19 curr_word, curr_steps = queue.popleft() 20 if curr_word == endWord: 21 return curr_steps 22 # List all possibilities 23 for i in range(L): 24 key = curr_word[:i] + "*" + curr_word[i+1:] 25 if key in adjacency_map: 26 neighbors = adjacency_map[key] 27 for neighbor in neighbors: 28 if neighbor == endWord: 29 return curr_steps + 1 30 if neighbor not in visited: 31 queue.append((neighbor, curr_steps + 1)) 32 visited.add(neighbor) 33 adjacency_map[key] = {} 34 return 0