Difficulty: Medium

Problem Description

Given two words ``` word1 ``` and ``` word2 ``` , and a dictionary's ``` Dict ``` , write a program to find the length of the shortest transformation sequence from ``` word1 ``` to ``` word2 ``` , such that:

• Adjacent words in the chain only differ by one character.
• Each transformed word must exist in the dictionary. Note that ``` word1 ``` is not a transformed word but ``` word2 ``` must be in the ``` Dict ``` .

Problem Note:

• Return ``` 0 ``` if there is no such transformation sequence.
• All words have the same length.
• All words contain only lowercase alphabetic characters.
• You may assume no duplicates in the word list.
• You may assume ``` word1 ``` and ``` word2 ``` are non-empty and are not the same.

Example 1

``````Input: word1 = "fit", word2 = "dog",
Dict = ["hit","hot","cot","dog","dot","log","cog"]
Output: 5
Explanation: As one shortest transformation is:
"fit"->"hit"->"hot"->"dot"->"dog"
Return its length which is 5.
Notice "fit" is not in Dict but "dog" is.``````

Example 2

``````Input: word1 = "fool", word2 = "sage",
Dict = ["cool","pool","poll","foil","fail","pole", "pope", "pale", "page", "sage", "sale", "fall"]``````
``````Output: 7
Explanation: As one shortest transformation is :
"fool"->"pool"->"poll"->"pole"->"pale"->"page"->"sage", Return its length which is 7.``````

#### Understanding The Problem

We are given a ``` beginWord ``` and an ``` endWord ``` . Let these two represent ``` start node ``` and ``` end node ``` of a graph. To reach ``` startnode ``` to the ``` endnode ``` we can use intermediate nodes. However, the intermediate node could only be from the given input ``` Dict ``` . We can move to all those nodes from the current node, if the current word is changed by a single letter.

Example 1 could be seen as →

So, this clears that we have to transform the input ``` dict ``` into some graph where the words represent each node and the undirected edges would be according to the condition.

#### Solution

• Breadth-First Search: Since only one letter can be changed at a time. The idea is simply to start from the ``` beginWord ``` , then visit its neighbors, then the non-visited neighbors of its neighbors until we arrive at the ``` endWord ``` . This is a typical BFS structure.

You may try to solve this problem here .

If we can construct a graph from the words in the given ``` dict ``` , then the problem could be solved using ``` Breadth First Search ``` .

First we have to construct the graph and for that we need to find out the neighbors of each node and to efficiently do that, we can do some pre-processing on the words of the given ``` wordList ``` . This pre-processing could be replacing the letter of a word by a non-alphabet say, ``` * ``` .

This pre-processing helps to form generic states to represent a single letter change.

For e.g. ``` Log --> *og <-- Dog ```

The preprocessing step helps us find out the generic one letter away nodes for any word of the word list and hence making it easier and quicker to get the adjacent nodes. Instead, we could iterate over the entire word list and check for the words that differ by one letter and that will increase the running time complexity. This step will help build the adjacency list.

For eg. While doing BFS if we have to find the adjacent nodes for ``` Dug ``` we can first find all the generic states for ``` Dug ``` .

1. ``` Dug => *ug ```
2. ``` Dug => D*g ```
3. ``` Dug => Du* ```

The second transformation ``` D*g ``` could then be mapped to ``` Dog ``` or ``` Dig ``` , since all of them share the same generic state. Having a common generic transformation means two words are connected and differ by one letter.

Now, Start from ``` beginWord ``` and search the ``` endWord ``` using BFS.

Solution Steps

1. Find all the possible generic/intermediate states using the words of ``` Dict ``` . Save the intermediate states in a dictionary with key as the intermediate word and value as the list of words that have the same intermediate word.
2. Push a tuple containing the ``` beginWord ``` and ``` 1 ``` in a queue. The ``` 1 ``` will represent the level number of a node. We have to return the level of the ``` endNode ``` as that would represent the shortest sequence/distance from the ``` beginWord ``` .
3. Use a visited dictionary to keep track of visited nodes. You may also remove the words from the input ``` dict ``` for every visited word.
4. Get the front element of the queue as ``` current_word ``` .
5. Find all the generic transformations of the ``` current_word ``` and find out if any of these transformations is also a transformation of other words in the word list. This is achieved by checking the ``` dict ``` .
6. The list of words we get from ``` dict ``` are all the words that have a common intermediate state with the ``` current_word ``` . These new set of words will be the adjacent nodes/words to ``` current_word ``` and hence added to the queue.
7. For each word in this list of intermediate words, append ``` (word, level + 1) ``` into the queue where ``` level ``` is the level for the ``` current_word ``` .
8. Eventually, you reach the desired word, its level would represent the shortest transformation sequence length.

Note : The termination condition here will be finding the ``` endword ```

Pseudo Code

``````bool isOneDiff(string A, string B) {
if (A.length() != B.length())
return false
int diff = 0
for (int i = 0 to i < A.length()) {
if (A[i] != B[i])
diff = diff + 1
if (diff > 1)
return false
}
if (diff == 1)
return true
else
return false
}``````
``````int ladderLength(string beginWord, string endWord, set dict) {
if (beginWord or endWord not in dict)
return 0
queue(string, int) que
que.push(beginWord, 1)
dict.erase(beginWord)
while (not que.empty()) {
cur = que.front()
que.pop()
for (auto it in dict ) {
if (isOneDiff(cur.first, dict[it])) {
if (dict[it] == endWord)
// return the level of end word
return cur.second + 1
que.push(dict[it], cur.second + 1))
dict.erase(it)
}
}
}
return 0
}``````

Complexity Analysis

Time Complexity: O ( × N ), where M is the length of each word, and N is the total number of words in the input word list.

• For each word in the word list, we iterate over its length to find all the intermediate words corresponding to it. Since the length of each word is M and we have N words, the total number of iterations the algorithm takes to create ``` all_combo_dict ``` is M × N .
• Forming each of the intermediate words takes O ( M ) time because of the substring operation used to create the new string. This adds up to a complexity of O ( × N ).

Space Complexity: O ( × N ).

• For each of M intermediate words we save the original word of length M . This simply means, for every word we would need a space of to save all the transformations corresponding to it. Thus, ``` all_combo_dict ``` will need a total space of O ( × N ).
• ``` Visited ``` dictionary would need a space of O ( M × N ) as each word is of length M .
• The queue for BFS would need a space for all O ( N ) words and this would also result in a space complexity of O ( M × N ).

Combining the above steps, the overall space complexity is O ( × N ) + O ( M N ) + O ( M N ) = O ( × N ) space.

Critical Ideas to Think

• Do you think the following approach “ First converting Wordlist into adjacency list Graphical representation and then using Dijkstra algorithm for finding the shortest distance between beginWord and endWord works just fine ” will work for this problem? If yes, then do you think that the time complexity would be different than ``` O(M²×N) ``` ?
• Can you perform this BFS using recursion?
• What would be the complexity of lookup in the hash?

#### Suggested Problems to Solve

• Minimum steps to reach the target by a Knight
• The shortest path to reach one prime to others by changing a single digit at a time
• Check if it is possible to reach a number by making jumps of two given length
• Minimum Genetic Mutation

If you have any more approaches or you find an error/bug in the above solutions, please comment down below.

Happy Coding!

Enjoy Algorithms!