Feature #1: Minimum Moves
Implementing the "Minimum Moves" feature for our "Scrabble" project.
We'll cover the following...
Description
The first functionality we need to build will find the minimum number of conversions needed to transform the initial word into the final word. Both of these words will be of the same length.
We’ll be provided with an initial word, a final word, and a word group in the form of a list. Our code needs to pick the shortest sequence of words of the same length from the word group, starting with the initial word and ending at the final word. This sequence should be such that each consecutive word differs from the previous one in one letter only.
Here is an illustration of this process:
Solution
We can take each word in the word group as a vertex (node) in a graph where there is an edge between two vertices if the corresponding words differ in one letter only. What we need to do is to find the shortest path between the vertices representing the initial and final words.
BFS (Breadth-First Search) is known to optimally find the shortest path in an unweighted graph. Before we can apply BFS, we need to build the graph. Additionally, we have to find adjacent nodes in which the corresponding words only differ by one letter. This requires some preprocessing steps.
What would be all possible one-letter edits to a word, say, let
? The edit might be done in the first letter, the second letter, or the last letter. So, the new word could be _et
, l_t
, or le_
, where you could replace the _
with any alphabet, assuming that it forms a valid English word.
This step helps us to form intermediary states for multiple words with a difference of only one letter.
Using the above example, the word let
can have three intermediary states:
-
let -> _et
-
let -> l_t
-
let -> le_
The second state can be mapped to the words lot
, lit
, etc., because they share the same intermediary state of l_t
. Hence, let
can have lot
and lit
as neighboring or adjacent nodes.
Here is how we will implement this feature:
-
Perform the preprocessing steps for each word in our dictionary to find all its intermediary states. Then, create a dictionary
statesList
. Save these intermediary states as the key and the words that correspond to each state as a list of values. -
Create a tuple containing the initial word and 0 as
(initialWord, 0)
. The initial word will be the root node at which the BFS will begin, and our root node will always be at level0
. We need to return the level of the final node as that would be the shortest distance from the initial word. Next, push this tuple into a queue. -
To prevent cycles, we’ll use a dictionary to maintain visited nodes.
-
We will retrieve the latest word from the queue until it’s empty. For each word, say
currentWord
, check thestatesList
dictionary and do the following:-
Find all the word’s intermediary states.
-
For each state found in the above step, check if it is also a state for any other word present in the initial word dictionary.
-
-
The list of words we obtain from the above step will be the same for the
currentWord
. All the words on the list will be considered adjacent nodes to thecurrentWord
and, therefore, will be added to our queue. -
For each new word in the nodes adjacent to the
currentWord
, compute the tuple(word, level + 1)
, wherelevel
is the level of thecurrentWord
and incrementing this by1
introduces a new layer on which BFS will run. Append this new tuple into our queue. -
If the final word is found, the value of
level
...