Question:
Given two words (beginWord and endWord), and a dictionary's word list, find all shortest transformation sequence(s) 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.
Example 1:
Input: beginWord = "hit", endWord = "cog", wordList = ["hot","dot","dog","lot","log","cog"]
Output: [ ["hit","hot","dot","dog","cog"], ["hit","hot","lot","log","cog"] ]
My solution is based on this idea, but how do I analyze the time and space complexity of this solution?
1) Perform a BFS starting at beginWord by transforming every letter to one of 26 letters, and see if the transformed word is in the wordList, if so, put in queue.
2) During BFS, maintain a graph of {word:nextWord} for all valid next words
3) When a nextWord reaches endWord, do a backtracking DFS (pre-order traversal) on the graph to get all paths.
class Solution:
def findLadders(self, beginWord, endWord, wordList):
"""
:type beginWord: str
:type endWord: str
:type wordList: List[str]
:rtype: List[List[str]]
"""
wordListSet = set(wordList+[beginWord])
graph = collections.defaultdict(list)
q = set([beginWord])
count = 0
result = []
while q:
count +=1
newQ = set()
for word in q:
wordListSet.remove(word)
for word in q:
if word == endWord:
self.getAllPaths(graph, beginWord, endWord, result, [])
return result
for i in range(len(word)):
for sub in 'abcdefghijklmnopqrstuvwxyz':
if sub != word[i]:
newWord = word[:i] + sub + word[i+1:]
if newWord in wordListSet:
graph[word].append(newWord)
newQ.add(newWord)
q = newQ
return []
def getAllPaths(self, graph, node, target, result, output):
#This is just a backtracking pre-order traversal DFS on a DAG.
output.append(node)
if node==target:
result.append(output[:])
else:
for child in graph[node]:
self.getAllPaths(graph,child, target, result, output)
output.pop()
I have a hard time coming up with the time and space complexity of it. My contention:
Time: O(26*L*N + N
), where L
is average length of each word, and N
is the number of words in the wordList. Worst case here is every word transformed happens to be in the list, so each transformation needs 26 * length of word
. The DFS part is just O(N)
. So asymptotically it's just O(L*N)
Space: O(N)
Time Complexity is most commonly estimated by counting the number of elementary steps performed by any algorithm to finish execution. Like in the example above, for the first code the loop will run n number of times, so the time complexity will be n atleast and as the value of n will increase the time taken will also increase.
And since the algorithm's performance may vary with different types of input data, hence for an algorithm we usually use the worst-case Time complexity of an algorithm because that is the maximum time taken for any input size. Now lets tap onto the next big topic related to Time complexity, which is How to Calculate Time Complexity.
It's an asymptotic notation to represent the time complexity. We will study about it in detail in the next tutorial. Time Complexity is most commonly estimated by counting the number of elementary steps performed by any algorithm to finish execution.
The running time consists of N loops (iterative or recursive) that are logarithmic, thus the algorithm is a combination of linear and logarithmic. NOTE: In general, doing something with every item in one dimension is linear, doing something with every item in two dimensions is quadratic, and dividing the working area in half is logarithmic.
You won't find all simple paths because there might be alternative shortest paths to the end word. The simplest counterexample is as follows:
beginWord = aa,
endWord = bb
wordList = [aa, ab, ba, bb]
Your algorithm would miss the path aa -> ba -> bb
. In fact, it will always find at most one path.
The time complexity is indeed O(L * N)
as you wrote but the space complexity is O(L*N)
which is the space that your graph or wordList
occupies.
the answer should be O(L^2 * n)
In the process of building a new word, it costs O(L^2)
in total. Firstly we loop the current word, that costs O(L)
; then for building each new string: newWord = word[:i] + sub + word[i+1:]
, this cost another O(L)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With