1
+ from collections import defaultdict
2
+ class Solution (object ):
3
+ def __init__ (self ):
4
+ self .length = 0
5
+ # Dictionary to hold combination of words that can be formed,
6
+ # from any given word. By changing one letter at a time.
7
+ self .all_combo_dict = defaultdict (list )
8
+
9
+ def visitWordNode (self , queue , visited , others_visited ):
10
+ queue_size = len (queue )
11
+ for _ in range (queue_size ):
12
+ current_word = queue .popleft ()
13
+ for i in range (self .length ):
14
+ # Intermediate words for current word
15
+ intermediate_word = current_word [:i ] + "*" + current_word [i + 1 :]
16
+
17
+ # Next states are all the words which share the same intermediate state.
18
+ for word in self .all_combo_dict [intermediate_word ]:
19
+ # If the intermediate state/word has already been visited from the
20
+ # other parallel traversal this means we have found the answer.
21
+ if word in others_visited :
22
+ return visited [current_word ] + others_visited [word ]
23
+ if word not in visited :
24
+ # Save the level as the value of the dictionary, to save number of hops.
25
+ visited [word ] = visited [current_word ] + 1
26
+ queue .append (word )
27
+
28
+ return None
29
+
30
+ def ladderLength (self , beginWord , endWord , wordList ):
31
+ """
32
+ :type beginWord: str
33
+ :type endWord: str
34
+ :type wordList: List[str]
35
+ :rtype: int
36
+ """
37
+ if endWord not in wordList or not endWord or not beginWord or not wordList :
38
+ return 0
39
+
40
+ # Since all words are of same length.
41
+ self .length = len (beginWord )
42
+
43
+ for word in wordList :
44
+ for i in range (self .length ):
45
+ # Key is the generic word
46
+ # Value is a list of words which have the same intermediate generic word.
47
+ self .all_combo_dict [word [:i ] + "*" + word [i + 1 :]].append (word )
48
+
49
+ # Queues for birdirectional BFS
50
+ queue_begin = collections .deque ([beginWord ]) # BFS starting from beginWord
51
+ queue_end = collections .deque ([endWord ]) # BFS starting from endWord
52
+
53
+ # Visited to make sure we don't repeat processing same word
54
+ visited_begin = {beginWord : 1 }
55
+ visited_end = {endWord : 1 }
56
+ ans = None
57
+
58
+ # We do a birdirectional search starting one pointer from begin
59
+ # word and one pointer from end word. Hopping one by one.
60
+ while queue_begin and queue_end :
61
+
62
+ # Progress forward one step from the shorter queue
63
+ if len (queue_begin ) <= len (queue_end ):
64
+ ans = self .visitWordNode (queue_begin , visited_begin , visited_end )
65
+ else :
66
+ ans = self .visitWordNode (queue_end , visited_end , visited_begin )
67
+ if ans :
68
+ return ans
69
+
70
+ return 0
0 commit comments