Day 9 – Word Ladder

January LeetCoding Challenge 2021 (33 Part Series)

1 Day1 – Check Array Formation Through Concatenation
2 Week 1 Bonus – Palindrome Permutation
29 more parts…
3 Day 2 Find a Corresponding Node of a Binary Tree in a Clone of That Tree
4 Day 3 – Beautiful Arrangement
5 Day 4 – Merge Two Sorted Lists
6 Day 5 – Remove Duplicates from Sorted List II
7 Day 6 – Kth Missing Positive Number
8 Day 7 – Longest Substring Without Repeating Characters
9 Day 8 – Check If Two String Arrays are Equivalent
10 Day 9 – Word Ladder
11 Day 10 – Create Sorted Array through Instructions
12 Day 11 – Merge Sorted Array
13 Day 12 – Add Two Numbers
14 Day 13 – Boats to Save People
15 Day 14 – Minimum Operations to Reduce X to Zero
16 Week 2 Bonus – Find Root of N-Ary Tree
17 Day 15 – Get Maximum in Generated Array
18 Day 16 – Kth Largest Element in an Array
19 Day 17 – Count Sorted Vowel Strings
20 Day 18 – Max Number of K-Sum Pairs
21 Week 3 Bonus – Nested List Weight Sum
22 Day 19 – Longest Palindromic Substring
23 Day 20 – Valid Parentheses
24 Day 21 – Find the Most Competitive Subsequence
25 Day 22 – Determine if Two Strings Are Close
26 Day 23 – Sort the Matrix Diagonally
27 Day 24 – Merge k Sorted Lists
28 Day 25 – Check If All 1’s Are at Least Length K Places Away
29 Day 28 – Smallest String With A Given Numeric Value
30 Day 27 – Concatenation of Consecutive Binary Numbers
31 Day 29 – Vertical Order Traversal of a Binary Tree
32 Day 30 – Minimize Deviation in Array
33 Day 31 – Next Permutation

The Problem

Given two words beginWord and endWord, and a dictionary wordList, return the length of the shortest transformation sequence from beginWord to endWord, such that:

  • Only one letter can be changed at a time.
  • Each transformed word must exist in the word list.

Return 0 if there is no such transformation sequence.

Example 1:

Input: beginWord = "hit", endWord = "cog", wordList = ["hot","dot","dog","lot","log","cog"]
Output: 5
Explanation: As one shortest transformation is "hit" -> "hot" -> "dot" -> "dog" -> "cog", return its length 5.
Input: beginWord = "hit", endWord = "cog", wordList = ["hot","dot","dog","lot","log","cog"]
Output: 5
Explanation: As one shortest transformation is "hit" -> "hot" -> "dot" -> "dog" -> "cog", return its length 5.
Input: beginWord = "hit", endWord = "cog", wordList = ["hot","dot","dog","lot","log","cog"] Output: 5 Explanation: As one shortest transformation is "hit" -> "hot" -> "dot" -> "dog" -> "cog", return its length 5.

Enter fullscreen mode Exit fullscreen mode

Example 2:

Input: beginWord = "hit", endWord = "cog", wordList = ["hot","dot","dog","lot","log"]
Output: 0
Explanation: The endWord "cog" is not in wordList, therefore no possible transformation.
Input: beginWord = "hit", endWord = "cog", wordList = ["hot","dot","dog","lot","log"]
Output: 0
Explanation: The endWord "cog" is not in wordList, therefore no possible transformation.
Input: beginWord = "hit", endWord = "cog", wordList = ["hot","dot","dog","lot","log"] Output: 0 Explanation: The endWord "cog" is not in wordList, therefore no possible transformation.

Enter fullscreen mode Exit fullscreen mode

Constraints:

  • 1 <= beginWord.length <= 100
  • endWord.length == beginWord.length
  • 1 <= wordList.length <= 5000
  • wordList[i].length == beginWord.length
  • beginWord, endWord, and wordList[i] consist of lowercase English letters.
  • beginWord != endWord
  • All the strings in wordList are unique.

My Tests

<span>import</span> <span>pytest</span>
<span>from</span> <span>.Day9</span> <span>import</span> <span>Solution</span>
<span>s</span> <span>=</span> <span>Solution</span><span>()</span>
<span>@</span><span>pytest</span><span>.</span><span>mark</span><span>.</span><span>parametrize</span><span>(</span>
<span>"beginWord,endWord,wordList,expected"</span><span>,</span>
<span>[</span>
<span>(</span><span>"hit"</span><span>,</span> <span>"cog"</span><span>,</span> <span>[</span><span>"hot"</span><span>,</span> <span>"dot"</span><span>,</span> <span>"dog"</span><span>,</span> <span>"lot"</span><span>,</span> <span>"log"</span><span>,</span> <span>"cog"</span><span>],</span> <span>5</span><span>),</span>
<span>(</span><span>"hit"</span><span>,</span> <span>"cog"</span><span>,</span> <span>[</span><span>"hot"</span><span>,</span> <span>"dot"</span><span>,</span> <span>"dog"</span><span>,</span> <span>"lot"</span><span>,</span> <span>"log"</span><span>],</span> <span>0</span><span>),</span>
<span>(</span><span>"count"</span><span>,</span> <span>"court"</span><span>,</span> <span>[</span><span>"mount"</span><span>,</span> <span>"point"</span><span>],</span> <span>0</span><span>),</span>
<span>],</span>
<span>)</span>
<span>def</span> <span>test_ladder_length</span><span>(</span><span>beginWord</span><span>,</span> <span>endWord</span><span>,</span> <span>wordList</span><span>,</span> <span>expected</span><span>):</span>
<span>assert</span> <span>s</span><span>.</span><span>ladderLength</span><span>(</span><span>beginWord</span><span>,</span> <span>endWord</span><span>,</span> <span>wordList</span><span>)</span> <span>==</span> <span>expected</span>
<span>import</span> <span>pytest</span>
<span>from</span> <span>.Day9</span> <span>import</span> <span>Solution</span>

<span>s</span> <span>=</span> <span>Solution</span><span>()</span>


<span>@</span><span>pytest</span><span>.</span><span>mark</span><span>.</span><span>parametrize</span><span>(</span>
    <span>"beginWord,endWord,wordList,expected"</span><span>,</span>
    <span>[</span>
        <span>(</span><span>"hit"</span><span>,</span> <span>"cog"</span><span>,</span> <span>[</span><span>"hot"</span><span>,</span> <span>"dot"</span><span>,</span> <span>"dog"</span><span>,</span> <span>"lot"</span><span>,</span> <span>"log"</span><span>,</span> <span>"cog"</span><span>],</span> <span>5</span><span>),</span>        
        <span>(</span><span>"hit"</span><span>,</span> <span>"cog"</span><span>,</span> <span>[</span><span>"hot"</span><span>,</span> <span>"dot"</span><span>,</span> <span>"dog"</span><span>,</span> <span>"lot"</span><span>,</span> <span>"log"</span><span>],</span> <span>0</span><span>),</span>
        <span>(</span><span>"count"</span><span>,</span> <span>"court"</span><span>,</span> <span>[</span><span>"mount"</span><span>,</span> <span>"point"</span><span>],</span> <span>0</span><span>),</span>
    <span>],</span>
<span>)</span>
<span>def</span> <span>test_ladder_length</span><span>(</span><span>beginWord</span><span>,</span> <span>endWord</span><span>,</span> <span>wordList</span><span>,</span> <span>expected</span><span>):</span>
    <span>assert</span> <span>s</span><span>.</span><span>ladderLength</span><span>(</span><span>beginWord</span><span>,</span> <span>endWord</span><span>,</span> <span>wordList</span><span>)</span> <span>==</span> <span>expected</span>
import pytest from .Day9 import Solution s = Solution() @pytest.mark.parametrize( "beginWord,endWord,wordList,expected", [ ("hit", "cog", ["hot", "dot", "dog", "lot", "log", "cog"], 5), ("hit", "cog", ["hot", "dot", "dog", "lot", "log"], 0), ("count", "court", ["mount", "point"], 0), ], ) def test_ladder_length(beginWord, endWord, wordList, expected): assert s.ladderLength(beginWord, endWord, wordList) == expected

Enter fullscreen mode Exit fullscreen mode

My Solution

<span>from</span> <span>typing</span> <span>import</span> <span>List</span><span>,</span> <span>Dict</span>
<span>import</span> <span>collections</span>
<span>def</span> <span>getLadderLength</span><span>(</span>
<span>queue</span><span>,</span>
<span>visited</span><span>:</span> <span>Dict</span><span>[</span><span>str</span><span>,</span> <span>int</span><span>],</span>
<span>otherVisited</span><span>:</span> <span>Dict</span><span>[</span><span>str</span><span>,</span> <span>int</span><span>],</span>
<span>wordLength</span><span>:</span> <span>int</span><span>,</span>
<span>combos</span><span>:</span> <span>Dict</span><span>[</span><span>str</span><span>,</span> <span>List</span><span>[</span><span>str</span><span>]],</span>
<span>):</span>
<span>word</span><span>,</span> <span>level</span> <span>=</span> <span>queue</span><span>.</span><span>popleft</span><span>()</span>
<span>for</span> <span>i</span> <span>in</span> <span>range</span><span>(</span><span>wordLength</span><span>):</span>
<span>testWord</span> <span>=</span> <span>word</span><span>[:</span><span>i</span><span>]</span> <span>+</span> <span>"*"</span> <span>+</span> <span>word</span><span>[</span><span>i</span> <span>+</span> <span>1</span> <span>:]</span>
<span>if</span> <span>testWord</span> <span>in</span> <span>combos</span><span>:</span>
<span>for</span> <span>w</span> <span>in</span> <span>combos</span><span>[</span><span>testWord</span><span>]:</span>
<span>if</span> <span>w</span> <span>in</span> <span>otherVisited</span><span>:</span>
<span>return</span> <span>level</span> <span>+</span> <span>otherVisited</span><span>[</span><span>w</span><span>]</span>
<span>if</span> <span>w</span> <span>not</span> <span>in</span> <span>visited</span><span>:</span>
<span>visited</span><span>[</span><span>w</span><span>]</span> <span>=</span> <span>level</span> <span>+</span> <span>1</span>
<span>queue</span><span>.</span><span>append</span><span>((</span><span>w</span><span>,</span> <span>level</span> <span>+</span> <span>1</span><span>))</span>
<span>return</span> <span>0</span>
<span>def</span> <span>preProcessNeighbors</span><span>(</span><span>wordList</span><span>:</span> <span>List</span><span>[</span><span>str</span><span>],</span> <span>wordLength</span><span>:</span> <span>int</span><span>):</span>
<span>combos</span> <span>=</span> <span>{}</span>
<span>for</span> <span>word</span> <span>in</span> <span>wordList</span><span>:</span>
<span>for</span> <span>i</span> <span>in</span> <span>range</span><span>(</span><span>wordLength</span><span>):</span>
<span>testWord</span> <span>=</span> <span>word</span><span>[:</span><span>i</span><span>]</span> <span>+</span> <span>"*"</span> <span>+</span> <span>word</span><span>[</span><span>i</span> <span>+</span> <span>1</span> <span>:]</span>
<span>if</span> <span>testWord</span> <span>not</span> <span>in</span> <span>combos</span><span>:</span>
<span>combos</span><span>[</span><span>testWord</span><span>]</span> <span>=</span> <span>[]</span>
<span>combos</span><span>[</span><span>testWord</span><span>].</span><span>append</span><span>(</span><span>word</span><span>)</span>
<span>return</span> <span>combos</span>
<span>class</span> <span>Solution</span><span>:</span>
<span>def</span> <span>ladderLength</span><span>(</span><span>self</span><span>,</span> <span>beginWord</span><span>:</span> <span>str</span><span>,</span> <span>endWord</span><span>:</span> <span>str</span><span>,</span> <span>wordList</span><span>:</span> <span>List</span><span>[</span><span>str</span><span>])</span> <span>-></span> <span>int</span><span>:</span>
<span>if</span> <span>endWord</span> <span>not</span> <span>in</span> <span>wordList</span><span>:</span>
<span>return</span> <span>0</span>
<span>wl</span> <span>=</span> <span>len</span><span>(</span><span>beginWord</span><span>)</span>
<span>combos</span> <span>=</span> <span>preProcessNeighbors</span><span>(</span><span>wordList</span><span>,</span> <span>wl</span><span>)</span>
<span>qBegin</span> <span>=</span> <span>collections</span><span>.</span><span>deque</span><span>([(</span><span>beginWord</span><span>,</span> <span>1</span><span>)])</span>
<span>qEnd</span> <span>=</span> <span>collections</span><span>.</span><span>deque</span><span>([(</span><span>endWord</span><span>,</span> <span>1</span><span>)])</span>
<span>visitedBegin</span> <span>=</span> <span>{</span><span>beginWord</span><span>:</span> <span>1</span><span>}</span>
<span>visitedEnd</span> <span>=</span> <span>{</span><span>endWord</span><span>:</span> <span>1</span><span>}</span>
<span>ladderLength</span> <span>=</span> <span>0</span>
<span>while</span> <span>qBegin</span> <span>and</span> <span>qEnd</span><span>:</span>
<span>ladderLength</span> <span>=</span> <span>getLadderLength</span><span>(</span><span>qBegin</span><span>,</span> <span>visitedBegin</span><span>,</span> <span>visitedEnd</span><span>,</span> <span>wl</span><span>,</span> <span>combos</span><span>)</span>
<span>if</span> <span>ladderLength</span> <span>></span> <span>0</span><span>:</span>
<span>return</span> <span>ladderLength</span>
<span>ladderLength</span> <span>=</span> <span>getLadderLength</span><span>(</span><span>qEnd</span><span>,</span> <span>visitedEnd</span><span>,</span> <span>visitedBegin</span><span>,</span> <span>wl</span><span>,</span> <span>combos</span><span>)</span>
<span>if</span> <span>ladderLength</span> <span>></span> <span>0</span><span>:</span>
<span>return</span> <span>ladderLength</span>
<span>return</span> <span>0</span>
<span>from</span> <span>typing</span> <span>import</span> <span>List</span><span>,</span> <span>Dict</span>
<span>import</span> <span>collections</span>


<span>def</span> <span>getLadderLength</span><span>(</span>
    <span>queue</span><span>,</span>
    <span>visited</span><span>:</span> <span>Dict</span><span>[</span><span>str</span><span>,</span> <span>int</span><span>],</span>
    <span>otherVisited</span><span>:</span> <span>Dict</span><span>[</span><span>str</span><span>,</span> <span>int</span><span>],</span>
    <span>wordLength</span><span>:</span> <span>int</span><span>,</span>
    <span>combos</span><span>:</span> <span>Dict</span><span>[</span><span>str</span><span>,</span> <span>List</span><span>[</span><span>str</span><span>]],</span>
<span>):</span>
    <span>word</span><span>,</span> <span>level</span> <span>=</span> <span>queue</span><span>.</span><span>popleft</span><span>()</span>
    <span>for</span> <span>i</span> <span>in</span> <span>range</span><span>(</span><span>wordLength</span><span>):</span>
        <span>testWord</span> <span>=</span> <span>word</span><span>[:</span><span>i</span><span>]</span> <span>+</span> <span>"*"</span> <span>+</span> <span>word</span><span>[</span><span>i</span> <span>+</span> <span>1</span> <span>:]</span>
        <span>if</span> <span>testWord</span> <span>in</span> <span>combos</span><span>:</span>
            <span>for</span> <span>w</span> <span>in</span> <span>combos</span><span>[</span><span>testWord</span><span>]:</span>
                <span>if</span> <span>w</span> <span>in</span> <span>otherVisited</span><span>:</span>
                    <span>return</span> <span>level</span> <span>+</span> <span>otherVisited</span><span>[</span><span>w</span><span>]</span>
                <span>if</span> <span>w</span> <span>not</span> <span>in</span> <span>visited</span><span>:</span>
                    <span>visited</span><span>[</span><span>w</span><span>]</span> <span>=</span> <span>level</span> <span>+</span> <span>1</span>
                    <span>queue</span><span>.</span><span>append</span><span>((</span><span>w</span><span>,</span> <span>level</span> <span>+</span> <span>1</span><span>))</span>
    <span>return</span> <span>0</span>


<span>def</span> <span>preProcessNeighbors</span><span>(</span><span>wordList</span><span>:</span> <span>List</span><span>[</span><span>str</span><span>],</span> <span>wordLength</span><span>:</span> <span>int</span><span>):</span>
    <span>combos</span> <span>=</span> <span>{}</span>
    <span>for</span> <span>word</span> <span>in</span> <span>wordList</span><span>:</span>
        <span>for</span> <span>i</span> <span>in</span> <span>range</span><span>(</span><span>wordLength</span><span>):</span>
            <span>testWord</span> <span>=</span> <span>word</span><span>[:</span><span>i</span><span>]</span> <span>+</span> <span>"*"</span> <span>+</span> <span>word</span><span>[</span><span>i</span> <span>+</span> <span>1</span> <span>:]</span>
            <span>if</span> <span>testWord</span> <span>not</span> <span>in</span> <span>combos</span><span>:</span>
                <span>combos</span><span>[</span><span>testWord</span><span>]</span> <span>=</span> <span>[]</span>
            <span>combos</span><span>[</span><span>testWord</span><span>].</span><span>append</span><span>(</span><span>word</span><span>)</span>
    <span>return</span> <span>combos</span>


<span>class</span> <span>Solution</span><span>:</span>
    <span>def</span> <span>ladderLength</span><span>(</span><span>self</span><span>,</span> <span>beginWord</span><span>:</span> <span>str</span><span>,</span> <span>endWord</span><span>:</span> <span>str</span><span>,</span> <span>wordList</span><span>:</span> <span>List</span><span>[</span><span>str</span><span>])</span> <span>-></span> <span>int</span><span>:</span>
        <span>if</span> <span>endWord</span> <span>not</span> <span>in</span> <span>wordList</span><span>:</span>
            <span>return</span> <span>0</span>
        <span>wl</span> <span>=</span> <span>len</span><span>(</span><span>beginWord</span><span>)</span>
        <span>combos</span> <span>=</span> <span>preProcessNeighbors</span><span>(</span><span>wordList</span><span>,</span> <span>wl</span><span>)</span>

        <span>qBegin</span> <span>=</span> <span>collections</span><span>.</span><span>deque</span><span>([(</span><span>beginWord</span><span>,</span> <span>1</span><span>)])</span>
        <span>qEnd</span> <span>=</span> <span>collections</span><span>.</span><span>deque</span><span>([(</span><span>endWord</span><span>,</span> <span>1</span><span>)])</span>

        <span>visitedBegin</span> <span>=</span> <span>{</span><span>beginWord</span><span>:</span> <span>1</span><span>}</span>
        <span>visitedEnd</span> <span>=</span> <span>{</span><span>endWord</span><span>:</span> <span>1</span><span>}</span>
        <span>ladderLength</span> <span>=</span> <span>0</span>

        <span>while</span> <span>qBegin</span> <span>and</span> <span>qEnd</span><span>:</span>
            <span>ladderLength</span> <span>=</span> <span>getLadderLength</span><span>(</span><span>qBegin</span><span>,</span> <span>visitedBegin</span><span>,</span> <span>visitedEnd</span><span>,</span> <span>wl</span><span>,</span> <span>combos</span><span>)</span>
            <span>if</span> <span>ladderLength</span> <span>></span> <span>0</span><span>:</span>
                <span>return</span> <span>ladderLength</span>
            <span>ladderLength</span> <span>=</span> <span>getLadderLength</span><span>(</span><span>qEnd</span><span>,</span> <span>visitedEnd</span><span>,</span> <span>visitedBegin</span><span>,</span> <span>wl</span><span>,</span> <span>combos</span><span>)</span>
            <span>if</span> <span>ladderLength</span> <span>></span> <span>0</span><span>:</span>
                <span>return</span> <span>ladderLength</span>

        <span>return</span> <span>0</span>
from typing import List, Dict import collections def getLadderLength( queue, visited: Dict[str, int], otherVisited: Dict[str, int], wordLength: int, combos: Dict[str, List[str]], ): word, level = queue.popleft() for i in range(wordLength): testWord = word[:i] + "*" + word[i + 1 :] if testWord in combos: for w in combos[testWord]: if w in otherVisited: return level + otherVisited[w] if w not in visited: visited[w] = level + 1 queue.append((w, level + 1)) return 0 def preProcessNeighbors(wordList: List[str], wordLength: int): combos = {} for word in wordList: for i in range(wordLength): testWord = word[:i] + "*" + word[i + 1 :] if testWord not in combos: combos[testWord] = [] combos[testWord].append(word) return combos class Solution: def ladderLength(self, beginWord: str, endWord: str, wordList: List[str]) -> int: if endWord not in wordList: return 0 wl = len(beginWord) combos = preProcessNeighbors(wordList, wl) qBegin = collections.deque([(beginWord, 1)]) qEnd = collections.deque([(endWord, 1)]) visitedBegin = {beginWord: 1} visitedEnd = {endWord: 1} ladderLength = 0 while qBegin and qEnd: ladderLength = getLadderLength(qBegin, visitedBegin, visitedEnd, wl, combos) if ladderLength > 0: return ladderLength ladderLength = getLadderLength(qEnd, visitedEnd, visitedBegin, wl, combos) if ladderLength > 0: return ladderLength return 0

Enter fullscreen mode Exit fullscreen mode

Analysis

My Commentary

After doing this one I am currently experiencing a high level of hate for coding problems. I had actually solved a problem similar to this some time ago so I even had the benefit of working from memory in some aspects but it still drove me a little crazy.

Looking at the problem we know we need to create a path of words going from the beginWord to the endWord. Looking at the example it might be intuitive at first to imagine iterating over the list, check which words differ by one letter, count the transformations along the way. That quickly breaks down once you change the example to be a little more complex. My next intuition was to maybe have a map of the various paths and return the shortest. That would get way too messy though. What would you use as a key? Do you have to access every list too often?

If we need a path and we need to track path length then the data structure we need is clearly a graph. Each node is a word with the level the node is on the path between beginning and end. Each neighbour of a node must contain another word that differs by only one letter. That way we adhere to the requirement that each transformation changes only one letter.

The first thing we do is check if the endWord is in the list at all and return 0 if not.

Then we must preprocess the graph.

<span>def</span> <span>preProcessNeighbors</span><span>(</span><span>wordList</span><span>:</span> <span>List</span><span>[</span><span>str</span><span>],</span> <span>wordLength</span><span>:</span> <span>int</span><span>):</span>
<span>combos</span> <span>=</span> <span>{}</span>
<span>for</span> <span>word</span> <span>in</span> <span>wordList</span><span>:</span>
<span>for</span> <span>i</span> <span>in</span> <span>range</span><span>(</span><span>wordLength</span><span>):</span>
<span>testWord</span> <span>=</span> <span>word</span><span>[:</span><span>i</span><span>]</span> <span>+</span> <span>"*"</span> <span>+</span> <span>word</span><span>[</span><span>i</span> <span>+</span> <span>1</span> <span>:]</span>
<span>if</span> <span>testWord</span> <span>not</span> <span>in</span> <span>combos</span><span>:</span>
<span>combos</span><span>[</span><span>testWord</span><span>]</span> <span>=</span> <span>[]</span>
<span>combos</span><span>[</span><span>testWord</span><span>].</span><span>append</span><span>(</span><span>word</span><span>)</span>
<span>return</span> <span>combos</span>
<span>def</span> <span>preProcessNeighbors</span><span>(</span><span>wordList</span><span>:</span> <span>List</span><span>[</span><span>str</span><span>],</span> <span>wordLength</span><span>:</span> <span>int</span><span>):</span>
    <span>combos</span> <span>=</span> <span>{}</span>
    <span>for</span> <span>word</span> <span>in</span> <span>wordList</span><span>:</span>
        <span>for</span> <span>i</span> <span>in</span> <span>range</span><span>(</span><span>wordLength</span><span>):</span>
            <span>testWord</span> <span>=</span> <span>word</span><span>[:</span><span>i</span><span>]</span> <span>+</span> <span>"*"</span> <span>+</span> <span>word</span><span>[</span><span>i</span> <span>+</span> <span>1</span> <span>:]</span>
            <span>if</span> <span>testWord</span> <span>not</span> <span>in</span> <span>combos</span><span>:</span>
                <span>combos</span><span>[</span><span>testWord</span><span>]</span> <span>=</span> <span>[]</span>
            <span>combos</span><span>[</span><span>testWord</span><span>].</span><span>append</span><span>(</span><span>word</span><span>)</span>
    <span>return</span> <span>combos</span>
def preProcessNeighbors(wordList: List[str], wordLength: int): combos = {} for word in wordList: for i in range(wordLength): testWord = word[:i] + "*" + word[i + 1 :] if testWord not in combos: combos[testWord] = [] combos[testWord].append(word) return combos

Enter fullscreen mode Exit fullscreen mode

wordLength is pretty much a constant based on the constraints:

  • endWord.length == beginWord.length
  • wordList[i].length == beginWord.length

We iterate over every word in our word list. Then we generate versions of the word replacing each letter with a *. So for example, say the word is can. We generate 3 words: *an, c*n, ca*. We are going to use these as keys and create a data structure that looks like this:

<span>{</span><span> </span><span>"*an"</span><span>:</span><span> </span><span>[</span><span>"can"</span><span>],</span><span> </span><span>"c*n"</span><span>:</span><span> </span><span>[</span><span>"can"</span><span>],</span><span> </span><span>"ca*"</span><span>:</span><span> </span><span>[</span><span>"can"</span><span>]</span><span> </span><span>}</span><span> </span>
<span>{</span><span> </span><span>"*an"</span><span>:</span><span> </span><span>[</span><span>"can"</span><span>],</span><span> </span><span>"c*n"</span><span>:</span><span> </span><span>[</span><span>"can"</span><span>],</span><span> </span><span>"ca*"</span><span>:</span><span> </span><span>[</span><span>"can"</span><span>]</span><span> </span><span>}</span><span> </span>
{ "*an": ["can"], "c*n": ["can"], "ca*": ["can"] }

Enter fullscreen mode Exit fullscreen mode

What use is this?

Imagine we have a word list like this:

["hot", "dot", "dog", "cog"]
["hot", "dot", "dog", "cog"]
["hot", "dot", "dog", "cog"]

Enter fullscreen mode Exit fullscreen mode

If we use the preprocessing above we will end up with a data structure like this:

<span>{</span><span> </span><span>"*ot"</span><span>:</span><span> </span><span>[</span><span>"hot"</span><span>,</span><span> </span><span>"dot"</span><span>],</span><span> </span><span>"h*t"</span><span>:</span><span> </span><span>[</span><span>"hot"</span><span>],</span><span> </span><span>"ho*"</span><span>:</span><span> </span><span>[</span><span>"hot"</span><span>],</span><span> </span><span>"d*t"</span><span>:</span><span> </span><span>[</span><span>"dot"</span><span>],</span><span> </span><span>"do*"</span><span>:</span><span> </span><span>[</span><span>"dot"</span><span>,</span><span> </span><span>"dog"</span><span>],</span><span> </span><span>"*og"</span><span>:</span><span> </span><span>[</span><span>"dog"</span><span>,</span><span> </span><span>"cog"</span><span>],</span><span> </span><span>"c*g"</span><span>:</span><span> </span><span>[</span><span>"cog"</span><span>],</span><span> </span><span>"co*"</span><span>:</span><span> </span><span>[</span><span>"cog"</span><span>]</span><span> </span><span>}</span><span> </span>
<span>{</span><span> </span><span>"*ot"</span><span>:</span><span> </span><span>[</span><span>"hot"</span><span>,</span><span> </span><span>"dot"</span><span>],</span><span> </span><span>"h*t"</span><span>:</span><span> </span><span>[</span><span>"hot"</span><span>],</span><span> </span><span>"ho*"</span><span>:</span><span> </span><span>[</span><span>"hot"</span><span>],</span><span> </span><span>"d*t"</span><span>:</span><span> </span><span>[</span><span>"dot"</span><span>],</span><span> </span><span>"do*"</span><span>:</span><span> </span><span>[</span><span>"dot"</span><span>,</span><span> </span><span>"dog"</span><span>],</span><span> </span><span>"*og"</span><span>:</span><span> </span><span>[</span><span>"dog"</span><span>,</span><span> </span><span>"cog"</span><span>],</span><span> </span><span>"c*g"</span><span>:</span><span> </span><span>[</span><span>"cog"</span><span>],</span><span> </span><span>"co*"</span><span>:</span><span> </span><span>[</span><span>"cog"</span><span>]</span><span> </span><span>}</span><span> </span>
{ "*ot": ["hot", "dot"], "h*t": ["hot"], "ho*": ["hot"], "d*t": ["dot"], "do*": ["dot", "dog"], "*og": ["dog", "cog"], "c*g": ["cog"], "co*": ["cog"] }

Enter fullscreen mode Exit fullscreen mode

This will come in pretty handy when we’re trying to figure out the transformations.

Next step is to set up 2 queues to allow us to do BFS to find the shortest path. In my solution, I am initiating a search from both the beginning and the end. Honestly, it would not have occurred to me to do it that way except I have come across a problem like this before and saw this as a form of optimisation. I did try with and without this i.e. also doing the search only from the start. In the leetcode analysis, there wasn’t really much different but I am using the dual search since that’s what I learned and it doesn’t make it much more complicated.

So next step, setup 2 queues and 2 maps to track visited words (you can replace this with one to only traverse one way):

<span>qBegin</span> <span>=</span> <span>collections</span><span>.</span><span>deque</span><span>([(</span><span>beginWord</span><span>,</span> <span>1</span><span>)])</span>
<span>qEnd</span> <span>=</span> <span>collections</span><span>.</span><span>deque</span><span>([(</span><span>endWord</span><span>,</span> <span>1</span><span>)])</span>
<span>visitedBegin</span> <span>=</span> <span>{</span><span>beginWord</span><span>:</span> <span>1</span><span>}</span>
<span>visitedEnd</span> <span>=</span> <span>{</span><span>endWord</span><span>:</span> <span>1</span><span>}</span>
<span>qBegin</span> <span>=</span> <span>collections</span><span>.</span><span>deque</span><span>([(</span><span>beginWord</span><span>,</span> <span>1</span><span>)])</span>
<span>qEnd</span> <span>=</span> <span>collections</span><span>.</span><span>deque</span><span>([(</span><span>endWord</span><span>,</span> <span>1</span><span>)])</span>

<span>visitedBegin</span> <span>=</span> <span>{</span><span>beginWord</span><span>:</span> <span>1</span><span>}</span>
<span>visitedEnd</span> <span>=</span> <span>{</span><span>endWord</span><span>:</span> <span>1</span><span>}</span>
qBegin = collections.deque([(beginWord, 1)]) qEnd = collections.deque([(endWord, 1)]) visitedBegin = {beginWord: 1} visitedEnd = {endWord: 1}

Enter fullscreen mode Exit fullscreen mode

Traverse the queues using a while loop with popleft:

<span>while</span> <span>qBegin</span> <span>and</span> <span>qEnd</span><span>:</span>
<span>while</span> <span>qBegin</span> <span>and</span> <span>qEnd</span><span>:</span>            
while qBegin and qEnd:

Enter fullscreen mode Exit fullscreen mode

Get the word and the current level in the queue:

<span>word</span><span>,</span> <span>level</span> <span>=</span> <span>queue</span><span>.</span><span>popleft</span><span>()</span>
<span>word</span><span>,</span> <span>level</span> <span>=</span> <span>queue</span><span>.</span><span>popleft</span><span>()</span>
word, level = queue.popleft()

Enter fullscreen mode Exit fullscreen mode

Iterate over each letter in that word and generate the keys to use in that map we preprocessed earlier:

<span>word</span><span>,</span> <span>level</span> <span>=</span> <span>queue</span><span>.</span><span>popleft</span><span>()</span>
<span>for</span> <span>i</span> <span>in</span> <span>range</span><span>(</span><span>wordLength</span><span>):</span>
<span>testWord</span> <span>=</span> <span>word</span><span>[:</span><span>i</span><span>]</span> <span>+</span> <span>"*"</span> <span>+</span> <span>word</span><span>[</span><span>i</span> <span>+</span> <span>1</span> <span>:]</span>
<span>word</span><span>,</span> <span>level</span> <span>=</span> <span>queue</span><span>.</span><span>popleft</span><span>()</span>
    <span>for</span> <span>i</span> <span>in</span> <span>range</span><span>(</span><span>wordLength</span><span>):</span>
        <span>testWord</span> <span>=</span> <span>word</span><span>[:</span><span>i</span><span>]</span> <span>+</span> <span>"*"</span> <span>+</span> <span>word</span><span>[</span><span>i</span> <span>+</span> <span>1</span> <span>:]</span>
word, level = queue.popleft() for i in range(wordLength): testWord = word[:i] + "*" + word[i + 1 :]

Enter fullscreen mode Exit fullscreen mode

Now we check that map e.g. if we generated a word like “*ot”, based on our example earlier, we’d get back a list like: ["hot", "dot"]

Recall:

<span>{</span><span> </span><span>"*ot"</span><span>:</span><span> </span><span>[</span><span>"hot"</span><span>,</span><span> </span><span>"dot"</span><span>],</span><span> </span><span>etc...</span><span> </span><span>}</span><span> </span>
<span>{</span><span> </span><span>"*ot"</span><span>:</span><span> </span><span>[</span><span>"hot"</span><span>,</span><span> </span><span>"dot"</span><span>],</span><span> </span><span>etc...</span><span> </span><span>}</span><span> </span>
{ "*ot": ["hot", "dot"], etc... }

Enter fullscreen mode Exit fullscreen mode

So in the following code:

<span>if</span> <span>testWord</span> <span>in</span> <span>combos</span><span>:</span>
<span>for</span> <span>w</span> <span>in</span> <span>combos</span><span>[</span><span>testWord</span><span>]:</span>
<span>if</span> <span>w</span> <span>in</span> <span>otherVisited</span><span>:</span>
<span>return</span> <span>level</span> <span>+</span> <span>otherVisited</span><span>[</span><span>w</span><span>]</span>
<span>if</span> <span>w</span> <span>not</span> <span>in</span> <span>visited</span><span>:</span>
<span>visited</span><span>[</span><span>w</span><span>]</span> <span>=</span> <span>level</span> <span>+</span> <span>1</span>
<span>queue</span><span>.</span><span>append</span><span>((</span><span>w</span><span>,</span> <span>level</span> <span>+</span> <span>1</span><span>))</span>
<span>if</span> <span>testWord</span> <span>in</span> <span>combos</span><span>:</span>
            <span>for</span> <span>w</span> <span>in</span> <span>combos</span><span>[</span><span>testWord</span><span>]:</span>
                <span>if</span> <span>w</span> <span>in</span> <span>otherVisited</span><span>:</span>
                    <span>return</span> <span>level</span> <span>+</span> <span>otherVisited</span><span>[</span><span>w</span><span>]</span>
                <span>if</span> <span>w</span> <span>not</span> <span>in</span> <span>visited</span><span>:</span>
                    <span>visited</span><span>[</span><span>w</span><span>]</span> <span>=</span> <span>level</span> <span>+</span> <span>1</span>
                    <span>queue</span><span>.</span><span>append</span><span>((</span><span>w</span><span>,</span> <span>level</span> <span>+</span> <span>1</span><span>))</span>
if testWord in combos: for w in combos[testWord]: if w in otherVisited: return level + otherVisited[w] if w not in visited: visited[w] = level + 1 queue.append((w, level + 1))

Enter fullscreen mode Exit fullscreen mode

We would iterate over that list. If we discovered that word had already been visited in the other search, we would know that is was part of the path and we would have our answer return level + otherVisited[w].

If it was not visited, we would mark it as visited on this end visited[w] = level + 1 and add the word to the queue queue.append((w, level + 1)) and continue the search.

The time complexity here is basically the length of every word squared multiplied by the number of words. So if N is the number of word sand K is word length, we’d have:

O(K^2 * N)

The space complexity is basically the same. We stored K^2 * N in that map.

My solution here is not great I think. The coded is messy and the performance could certainly be optimised a lot more (only beat 50% of other submissions). Other things bother me like the method name I used getLadderLength doesn’t make sense but my brain is fried at this point so leaving it here. I hope I don’t ever get one like this in an interview!

January LeetCoding Challenge 2021 (33 Part Series)

1 Day1 – Check Array Formation Through Concatenation
2 Week 1 Bonus – Palindrome Permutation
29 more parts…
3 Day 2 Find a Corresponding Node of a Binary Tree in a Clone of That Tree
4 Day 3 – Beautiful Arrangement
5 Day 4 – Merge Two Sorted Lists
6 Day 5 – Remove Duplicates from Sorted List II
7 Day 6 – Kth Missing Positive Number
8 Day 7 – Longest Substring Without Repeating Characters
9 Day 8 – Check If Two String Arrays are Equivalent
10 Day 9 – Word Ladder
11 Day 10 – Create Sorted Array through Instructions
12 Day 11 – Merge Sorted Array
13 Day 12 – Add Two Numbers
14 Day 13 – Boats to Save People
15 Day 14 – Minimum Operations to Reduce X to Zero
16 Week 2 Bonus – Find Root of N-Ary Tree
17 Day 15 – Get Maximum in Generated Array
18 Day 16 – Kth Largest Element in an Array
19 Day 17 – Count Sorted Vowel Strings
20 Day 18 – Max Number of K-Sum Pairs
21 Week 3 Bonus – Nested List Weight Sum
22 Day 19 – Longest Palindromic Substring
23 Day 20 – Valid Parentheses
24 Day 21 – Find the Most Competitive Subsequence
25 Day 22 – Determine if Two Strings Are Close
26 Day 23 – Sort the Matrix Diagonally
27 Day 24 – Merge k Sorted Lists
28 Day 25 – Check If All 1’s Are at Least Length K Places Away
29 Day 28 – Smallest String With A Given Numeric Value
30 Day 27 – Concatenation of Consecutive Binary Numbers
31 Day 29 – Vertical Order Traversal of a Binary Tree
32 Day 30 – Minimize Deviation in Array
33 Day 31 – Next Permutation

原文链接:Day 9 – Word Ladder

© 版权声明
THE END
喜欢就支持一下吧
点赞10 分享
No one can change another. But one can be the reason for another to change.
没人能改变另一个人,但是某个人能成为一个人改变的原因
评论 抢沙发

请登录后发表评论

    暂无评论内容