Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
4d81400
feat: add lowest_common_ancestor_of_a_binary_tree
wislertt Sep 6, 2025
32e2f2c
feat: update dict tree
wislertt Sep 6, 2025
93834e5
feat: add word ladder
wislertt Sep 6, 2025
d40fce4
feat: add Serialize and Deserialize Binary Tree
wislertt Sep 6, 2025
19374dc
feat: add find_median_from_data_stream
wislertt Sep 6, 2025
7eff232
feat: add Basic Calculator
wislertt Sep 6, 2025
ad6325c
feat: add Merge k Sorted Lists
wislertt Sep 6, 2025
e586905
feat: add DoublyListNode
wislertt Sep 6, 2025
5b27fa9
feat: add Ransom Note
wislertt Sep 6, 2025
f0a2a39
feat: add First Bad Version
wislertt Sep 6, 2025
49412c3
feat: Climbing Stairs
wislertt Sep 6, 2025
a93e761
feat: add Majority Element
wislertt Sep 6, 2025
1e31975
feat: add Diameter of Binary Tree
wislertt Sep 6, 2025
63dce89
feat: add Middle of the Linked List
wislertt Sep 6, 2025
a7ad8ba
feat: add Contains Duplicate
wislertt Sep 6, 2025
6d4d393
feat: add Flood Fill
wislertt Sep 6, 2025
bd6d987
feat: add Valid Parentheses
wislertt Sep 6, 2025
8f83609
feat: add Balanced Binary Tree
wislertt Sep 6, 2025
0ed0402
feat: add Merge Two Sorted Lists
wislertt Sep 6, 2025
76581dc
feat: add Best Time to Buy and Sell Stock
wislertt Sep 6, 2025
a99f4c4
feat: add Implement Queue using Stacks
wislertt Sep 6, 2025
197c64b
feat: add 01 Matrix
wislertt Sep 6, 2025
d73819b
feat: add Course Schedule
wislertt Sep 7, 2025
b422baf
feat: add Rotting Oranges
wislertt Sep 7, 2025
61ef1f6
feat: add Permutations
wislertt Sep 7, 2025
96908f2
feat: add Word Break
wislertt Sep 7, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
feat: add word ladder
  • Loading branch information
wislertt committed Sep 6, 2025
commit 93834e5c51d69cd0e20256d80b56c31e943e0b75
47 changes: 47 additions & 0 deletions .templates/leetcode/json/word_ladder.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
{
"problem_name": "word_ladder",
"solution_class_name": "Solution",
"problem_number": "127",
"problem_title": "Word Ladder",
"difficulty": "Hard",
"topics": "Hash Table, String, Breadth-First Search",
"tags": ["grind-75"],
"readme_description": "A **transformation sequence** from word `beginWord` to word `endWord` using a dictionary `wordList` is a sequence of words `beginWord -> s1 -> s2 -> ... -> sk` such that:\n\n- Every adjacent pair of words differs by a single letter.\n- Every `si` for `1 <= i <= k` is in `wordList`. Note that `beginWord` does not need to be in `wordList`.\n- `sk == endWord`\n\nGiven two words, `beginWord` and `endWord`, and a dictionary `wordList`, return the **number of words** in the **shortest transformation sequence** from `beginWord` to `endWord`, or `0` if no such sequence exists.",
"readme_examples": [
{
"content": "```\nInput: beginWord = \"hit\", endWord = \"cog\", wordList = [\"hot\",\"dot\",\"dog\",\"lot\",\"log\",\"cog\"]\nOutput: 5\n```\n**Explanation:** One shortest transformation sequence is \"hit\" -> \"hot\" -> \"dot\" -> \"dog\" -> \"cog\", which is 5 words long."
},
{
"content": "```\nInput: beginWord = \"hit\", endWord = \"cog\", wordList = [\"hot\",\"dot\",\"dog\",\"lot\",\"log\"]\nOutput: 0\n```\n**Explanation:** The endWord \"cog\" is not in wordList, therefore there is no valid transformation sequence."
}
],
"readme_constraints": "- 1 <= beginWord.length <= 10\n- endWord.length == beginWord.length\n- 1 <= wordList.length <= 5000\n- wordList[i].length == beginWord.length\n- beginWord, endWord, and wordList[i] consist of lowercase English letters.\n- beginWord != endWord\n- All the words in wordList are unique.",
"readme_additional": "",
"solution_imports": "",
"solution_methods": [
{
"name": "ladder_length",
"parameters": "begin_word: str, end_word: str, word_list: list[str]",
"return_type": "int",
"dummy_return": "0"
}
],
"test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .solution import Solution",
"test_class_name": "WordLadder",
"test_helper_methods": [
{ "name": "setup_method", "parameters": "", "body": "self.solution = Solution()" }
],
"test_methods": [
{
"name": "test_ladder_length",
"parametrize": "begin_word, end_word, word_list, expected",
"parametrize_typed": "begin_word: str, end_word: str, word_list: list[str], expected: int",
"test_cases": "[('hit', 'cog', ['hot', 'dot', 'dog', 'lot', 'log', 'cog'], 5), ('hit', 'cog', ['hot', 'dot', 'dog', 'lot', 'log'], 0), ('a', 'c', ['a', 'b', 'c'], 2)]",
"body": "result = self.solution.ladder_length(begin_word, end_word, word_list)\nassert result == expected"
}
],
"playground_imports": "from solution import Solution",
"playground_test_case": "# Example test case\nbegin_word = 'hit'\nend_word = 'cog'\nword_list = ['hot', 'dot', 'dog', 'lot', 'log', 'cog']\nexpected = 5",
"playground_execution": "result = Solution().ladder_length(begin_word, end_word, word_list)\nresult",
"playground_assertion": "assert result == expected"
}
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
PYTHON_VERSION = 3.13
PROBLEM ?= lowest_common_ancestor_of_a_binary_search_tree
PROBLEM ?= word_ladder
FORCE ?= 0
COMMA := ,

Expand Down
47 changes: 47 additions & 0 deletions leetcode/word_ladder/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Word Ladder

**Difficulty:** Hard
**Topics:** Hash Table, String, Breadth-First Search
**Tags:** grind-75

**LeetCode:** [Problem 127](https://leetcode.com/problems/word-ladder/description/)

## Problem Description

A **transformation sequence** from word `beginWord` to word `endWord` using a dictionary `wordList` is a sequence of words `beginWord -> s1 -> s2 -> ... -> sk` such that:

- Every adjacent pair of words differs by a single letter.
- Every `si` for `1 <= i <= k` is in `wordList`. Note that `beginWord` does not need to be in `wordList`.
- `sk == endWord`

Given two words, `beginWord` and `endWord`, and a dictionary `wordList`, return the **number of words** in the **shortest transformation sequence** from `beginWord` to `endWord`, or `0` if no such sequence exists.

## Examples

### Example 1:

```
Input: beginWord = "hit", endWord = "cog", wordList = ["hot","dot","dog","lot","log","cog"]
Output: 5
```

**Explanation:** One shortest transformation sequence is "hit" -> "hot" -> "dot" -> "dog" -> "cog", which is 5 words long.

### Example 2:

```
Input: beginWord = "hit", endWord = "cog", wordList = ["hot","dot","dog","lot","log"]
Output: 0
```

**Explanation:** The endWord "cog" is not in wordList, therefore there is no valid transformation sequence.

## Constraints

- 1 <= beginWord.length <= 10
- 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 words in wordList are unique.
Empty file.
81 changes: 81 additions & 0 deletions leetcode/word_ladder/playground.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"id": "imports",
"metadata": {},
"outputs": [],
"source": [
"from solution import Solution"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "setup",
"metadata": {},
"outputs": [],
"source": [
"# Example test case\n",
"begin_word = \"hit\"\n",
"end_word = \"cog\"\n",
"word_list = [\"hot\", \"dot\", \"dog\", \"lot\", \"log\", \"cog\"]\n",
"expected = 5"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "execute",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"5"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"result = Solution().ladder_length(begin_word, end_word, word_list)\n",
"result"
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "test",
"metadata": {},
"outputs": [],
"source": [
"assert result == expected"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "leetcode-py-py3.13",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.13.7"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
33 changes: 33 additions & 0 deletions leetcode/word_ladder/solution.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
class Solution:
# Time: O(M^2 * N) where M is length of each word, N is total number of words
# Space: O(M * N) for the visited sets
def ladder_length(self, begin_word: str, end_word: str, word_list: list[str]) -> int:
if end_word not in word_list:
return 0

word_set = set(word_list)
begin_set = {begin_word}
end_set = {end_word}
length = 1

while begin_set and end_set:
if len(begin_set) > len(end_set):
begin_set, end_set = end_set, begin_set

next_set = set()
for word in begin_set:
for i in range(len(word)):
for c in "abcdefghijklmnopqrstuvwxyz":
new_word = word[:i] + c + word[i + 1 :]

if new_word in end_set:
return length + 1

if new_word in word_set:
next_set.add(new_word)
word_set.remove(new_word)

begin_set = next_set
length += 1

return 0
42 changes: 42 additions & 0 deletions leetcode/word_ladder/tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import pytest

from leetcode_py.test_utils import logged_test

from .solution import Solution


class TestWordLadder:
def setup_method(self):
self.solution = Solution()

@pytest.mark.parametrize(
"begin_word, end_word, word_list, expected",
[
# Basic cases
("hit", "cog", ["hot", "dot", "dog", "lot", "log", "cog"], 5),
("hit", "cog", ["hot", "dot", "dog", "lot", "log"], 0),
("a", "c", ["a", "b", "c"], 2),
# Edge cases
("hot", "dog", ["hot", "dog"], 0), # No intermediate words
("hot", "hot", ["hot"], 1), # Same word
("cat", "dog", [], 0), # Empty word list
("cat", "dog", ["cat"], 0), # End word not in list
# Single character changes
("a", "b", ["a", "b"], 2),
("ab", "cd", ["ab", "ad", "cd"], 3),
# Longer paths
("red", "tax", ["ted", "tex", "red", "tax", "tad", "den", "rex", "pee"], 4),
# Multiple possible paths (should return shortest)
("cat", "dog", ["cat", "bat", "bag", "dag", "dog", "cag", "cog"], 4),
# No path exists
("abc", "def", ["abc", "def", "ghi"], 0),
# Direct transformation
("cat", "bat", ["cat", "bat"], 2),
# Longer word length
("word", "form", ["word", "worm", "form", "foam", "flam", "flab"], 3),
],
)
@logged_test
def test_ladder_length(self, begin_word: str, end_word: str, word_list: list[str], expected: int):
result = self.solution.ladder_length(begin_word, end_word, word_list)
assert result == expected