(Source) 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
The idea is using Breadth-first Search
, and searching from the “smaller” side in each turn.
class Solution1(object):
def ladderLength(self, beginWord, endWord, wordList):
"""
:type beginWord: str
:type endWord: str
:type wordlist: Set[str]
:rtype: List[List[int]]
"""
st1, st2 = set([beginWord]), set([endWord])
words = set(wordList)
explored = set([beginWord, endWord])
length = 2
while True:
if len(st1) > len(st2):
st1, st2 = st2, st1
st = set()
while st1:
s1 = st1.pop()
for s2 in words:
if self.valid(s1, s2):
if s2 in st2:
return length
elif s2 in explored:
pass
else:
st.add(s2)
explored.add(s2)
st1 = st
length += 1
return 0
def valid(self, s1, s2):
"""
:type s1: str
:type s2: str
:rtype: bool
"""
return sum(1 for i in xrange(len(s1)) if s1[i] != s2[i]) == 1
Solution above gets an TLE
, the reason is the inner while and for loop doing the Breadth-first Search. Noting that “All words contain only lowercase alphabetic characters“, above algorithm can be further optimised as below:
class Solution(object):
def ladderLength(self, beginWord, endWord, wordList):
"""
:type beginWord: str
:type endWord: str
:type wordlist: Set[str]
:rtype: List[List[int]]
"""
st1, st2 = set([beginWord]), set([endWord])
words = set(wordList)
explored = set([beginWord, endWord])
length = 2
while st1:
st1, st2 = sorted([st1, st2], key=len)
st = set()
while st1:
s1 = st1.pop()
arr = list(s1)
for i in xrange(len(arr)):
old_ord_value = ord(arr[i])
for ord_value in xrange(97, 123):
if ord_value == old_ord_value:
continue
arr[i] = chr(ord_value)
s2 = ''.join(arr)
if s2 in st2:
return length
elif s2 not in explored and s2 in words:
st.add(s2)
explored.add(s2)
arr[i] = chr(old_ord_value)
st1 = st
length += 1
return 0