Word Ladder hard
Problem Statement
Given two words, beginWord
and endWord
, and a word list 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: hit -> hot -> dot -> dog -> cog
Example 2:
Input: beginWord = "hit"
, endWord = "cog"
, wordList = ["hot","dot","dog","lot","log"]
Output: 0
Explanation: cog
is not in wordList
.
Steps
-
Initialization: Add
beginWord
to a queueq
and a setvisited
. Initialize the transformation sequence lengthlevel
to 1. -
Breadth-First Search (BFS): While the queue is not empty, perform the following steps:
- Dequeue a word
currentWord
fromq
. - Iterate through each word in
wordList
. - For each word, check if it differs from
currentWord
by only one letter and if it's not invisited
. - If both conditions are true, enqueue the word to
q
, add it tovisited
, and incrementlevel
.
- Dequeue a word
-
Termination: If
endWord
is found invisited
, returnlevel
. If the queue becomes empty before findingendWord
, it means there's no transformation sequence, so return 0.
Explanation
The problem is essentially finding the shortest path in a graph where words are nodes and edges exist between words that differ by one letter. Breadth-First Search (BFS) is the ideal algorithm for finding the shortest path in an unweighted graph. BFS explores the graph level by level, ensuring that the first time it finds endWord
, it has found the shortest path. We use a visited
set to avoid cycles and redundant exploration. The level
variable keeps track of the length of the transformation sequence.
Code
#include <iostream>
#include <vector>
#include <queue>
#include <string>
#include <unordered_set>
using namespace std;
int ladderLength(string beginWord, string endWord, vector<string>& wordList) {
queue<string> q;
unordered_set<string> visited;
q.push(beginWord);
visited.insert(beginWord);
int level = 1;
unordered_set<string> wordSet;
for (const string& word : wordList) {
wordSet.insert(word);
}
if (wordSet.find(endWord) == wordSet.end()) return 0; //endWord not in wordList
while (!q.empty()) {
int size = q.size();
for (int i = 0; i < size; ++i) {
string currentWord = q.front();
q.pop();
if (currentWord == endWord) return level;
for (int j = 0; j < currentWord.length(); ++j) {
for (char k = 'a'; k <= 'z'; ++k) {
string nextWord = currentWord;
nextWord[j] = k;
if (wordSet.count(nextWord) && visited.find(nextWord) == visited.end()) {
q.push(nextWord);
visited.insert(nextWord);
}
}
}
}
level++;
}
return 0;
}
int main() {
string beginWord = "hit";
string endWord = "cog";
vector<string> wordList = {"hot", "dot", "dog", "lot", "log", "cog"};
cout << ladderLength(beginWord, endWord, wordList) << endl; // Output: 5
beginWord = "hit";
endWord = "cog";
wordList = {"hot","dot","dog","lot","log"};
cout << ladderLength(beginWord, endWord, wordList) << endl; // Output: 0
return 0;
}
Complexity
- Time Complexity: O(M * N * 26), where M is the length of the words and N is the number of words in
wordList
. In the worst case, we might explore all possible one-letter changes for every word in the queue. - Space Complexity: O(N), where N is the number of words in
wordList
. The space is dominated by thevisited
set and the queue.