Mutating counts without cleanup
Wrong move: Zero-count keys stay in map and break distinct/count constraints.
Usually fails on: Window/map size checks are consistently off by one.
Fix: Delete keys when count reaches zero.
Break down a hard problem into reliable checkpoints, edge-case handling, and complexity trade-offs.
A transformation sequence from word beginWord to word endWord using a dictionary wordList is a sequence of words beginWord -> s1 -> s2 -> ... -> sk such that:
si for 1 <= i <= k is in wordList. Note that beginWord does not need to be in wordList.sk == endWordGiven 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.
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 <= 10endWord.length == beginWord.length1 <= wordList.length <= 5000wordList[i].length == beginWord.lengthbeginWord, endWord, and wordList[i] consist of lowercase English letters.beginWord != endWordwordList are unique.Problem summary: 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.
Start with the most direct exhaustive search. That gives a correctness anchor before optimizing.
Pattern signal: Hash Map
"hit" "cog" ["hot","dot","dog","lot","log","cog"]
"hit" "cog" ["hot","dot","dog","lot","log"]
word-ladder-ii)minimum-genetic-mutation)words-within-two-edits-of-dictionary)Source-backed implementations are provided below for direct study and interview prep.
// Accepted solution for LeetCode #127: Word Ladder
class Solution {
public int ladderLength(String beginWord, String endWord, List<String> wordList) {
Set<String> words = new HashSet<>(wordList);
Queue<String> q = new ArrayDeque<>();
q.offer(beginWord);
int ans = 1;
while (!q.isEmpty()) {
++ans;
for (int i = q.size(); i > 0; --i) {
String s = q.poll();
char[] chars = s.toCharArray();
for (int j = 0; j < chars.length; ++j) {
char ch = chars[j];
for (char k = 'a'; k <= 'z'; ++k) {
chars[j] = k;
String t = new String(chars);
if (!words.contains(t)) {
continue;
}
if (endWord.equals(t)) {
return ans;
}
q.offer(t);
words.remove(t);
}
chars[j] = ch;
}
}
}
return 0;
}
}
// Accepted solution for LeetCode #127: Word Ladder
func ladderLength(beginWord string, endWord string, wordList []string) int {
words := make(map[string]bool)
for _, word := range wordList {
words[word] = true
}
q := []string{beginWord}
ans := 1
for len(q) > 0 {
ans++
for i := len(q); i > 0; i-- {
s := q[0]
q = q[1:]
chars := []byte(s)
for j := 0; j < len(chars); j++ {
ch := chars[j]
for k := 'a'; k <= 'z'; k++ {
chars[j] = byte(k)
t := string(chars)
if !words[t] {
continue
}
if t == endWord {
return ans
}
q = append(q, t)
words[t] = false
}
chars[j] = ch
}
}
}
return 0
}
# Accepted solution for LeetCode #127: Word Ladder
class Solution:
def ladderLength(self, beginWord: str, endWord: str, wordList: List[str]) -> int:
words = set(wordList)
q = deque([beginWord])
ans = 1
while q:
ans += 1
for _ in range(len(q)):
s = q.popleft()
s = list(s)
for i in range(len(s)):
ch = s[i]
for j in range(26):
s[i] = chr(ord('a') + j)
t = ''.join(s)
if t not in words:
continue
if t == endWord:
return ans
q.append(t)
words.remove(t)
s[i] = ch
return 0
// Accepted solution for LeetCode #127: Word Ladder
pub fn encode(word : String) -> u64 {
word.into_bytes().into_iter().fold(0, |mut acc, c|{
acc <<= 5;
acc | (c - b'a' + 1) as u64
})
}
use std::collections::HashSet;
use std::collections::VecDeque;
impl Solution {
pub fn ladder_length(begin_word: String, end_word: String, word_list: Vec<String>) -> i32 {
let word_len = begin_word.len();
let begin_word = encode(begin_word);
let end_word = encode(end_word);
let mut word_list : HashSet<u64> = word_list.into_iter().map(|word| encode(word)).collect();
let mut frontier : VecDeque<u64> = VecDeque::with_capacity(5000);
let mut seen : HashSet<u64> = HashSet::new();
let mut res = 1;
frontier.push_back(begin_word);
while !frontier.is_empty() {
let len = frontier.len();
for _ in 0..len {
let curr = frontier.pop_front().unwrap();
if curr == end_word {
return res;
}
for i in 0..word_len {
let filter = !(0b11111 << (i * 5));
for character in 1..=26 {
let neighbour = ((curr & filter) | (character << (i * 5)));
if word_list.contains(&neighbour) && !seen.contains(&neighbour) {
frontier.push_back(neighbour);
seen.insert(neighbour);
}
}
}
}
res += 1;
}
0
}
}
// Accepted solution for LeetCode #127: Word Ladder
function ladderLength(beginWord: string, endWord: string, wordList: string[]): number {
if (!wordList.includes(endWord)) return 0;
const replace = (s: string, i: number, ch: string) => s.slice(0, i) + ch + s.slice(i + 1);
const { length } = beginWord;
const words: Record<string, string[]> = {};
const g: Record<string, string[]> = {};
for (const w of [beginWord, ...wordList]) {
const derivatives: string[] = [];
for (let i = 0; i < length; i++) {
const nextW = replace(w, i, '*');
derivatives.push(nextW);
g[nextW] ??= [];
g[nextW].push(w);
}
words[w] = derivatives;
}
let ans = 0;
let q = words[beginWord];
const vis = new Set<string>([beginWord]);
while (q.length) {
const nextQ: string[] = [];
ans++;
for (const variant of q) {
for (const w of g[variant]) {
if (w === endWord) return ans + 1;
if (vis.has(w)) continue;
vis.add(w);
nextQ.push(...words[w]);
}
}
q = nextQ;
}
return 0;
}
Use this to step through a reusable interview workflow for this problem.
For each element, scan the rest of the array looking for a match. Two nested loops give n × (n−1)/2 comparisons = O(n²). No extra space since we only use loop indices.
One pass through the input, performing O(1) hash map lookups and insertions at each step. The hash map may store up to n entries in the worst case. This is the classic space-for-time tradeoff: O(n) extra memory eliminates an inner loop.
Review these before coding to avoid predictable interview regressions.
Wrong move: Zero-count keys stay in map and break distinct/count constraints.
Usually fails on: Window/map size checks are consistently off by one.
Fix: Delete keys when count reaches zero.