A transformation sequence from word beginWord to word endWord using a dictionary
wordList is a sequence of words beginWord -> -> -> … ->
such that:
- Every adjacent pair of words differs by a single letter.
- Every for is in
wordList. Note thatbeginWorddoes not need to be inwordList. - ==
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 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.
Approach 1: Breadth First Search
Idea
The solution to this problem is similar to this one: Open the Lock.
Since only one character can be changed a time, we can change the characters one
at a time and check if it exists in the wordList. Then, we can assume the word
as a neighbor to the previous word.
For the first example - [“hot”,“dot”,“dog”,“lot”,“log”,“cog”], neighbors of “hot” is ”dot” and ”lot”.
Now, we can run a BFS starting from the beginWord and the level where endWord
exists, is the length of the shortest transformation sequence.
Implementation
In C++:
int ladderLength(string beginWord, string endWord, vector<string>& wordList) {
unordered_set<string> words (wordList.begin(), wordList.end());
// if `endWord` doesn't exist, return 0
if (!words.count(endWord)) return 0;
unordered_set<string> visited;
queue<string> q;
q.push(beginWord);
visited.insert(beginWord);
int level = 1;
while(!q.empty()) {
int size = q.size();
while(size--) {
string s = q.front();
q.pop();
// iterate over all the characters of `s`
for (int i = 0; i < (int)s.length(); ++i) {
string changed = s;
// change the characters from 'a' to 'z'
for (char c = 'a'; c <= 'z'; ++c) {
if (c == s[i]) continue;
changed[i] = c;
// pre-emptively check
if (changed == endWord) {
return 1 + level;
}
if (!visited.count(changed) and words.count(changed)) {
q.push(changed);
visited.insert(changed);
}
}
}
}
++level;
}
return 0;
}Complexity Analysis
- Time Complexity: where length of
wordListand average length of the strings. Look-up time forwordListhashset is . - Space Complexity: .
Approach 2: Bi-directional Breadth First Search
Idea
Since the start node and the target node are fixed, we can run bidirectional BFS (from both start and end) to improve the time complexity from to (where branching factor of the tree and distance of target from source).
Implementation
In C++:
int ladderLength(string beginWord, string endWord, vector<string>& wordList) {
unordered_set<string> begin({beginWord}),
end({endWord}),
words(wordList.begin(), wordList.end());
// if endWord doesn't exist, thus return 0
if(!words.count(endWord)) return 0;
int level = 1;
unordered_set<string> visited;
while(begin.size() and end.size()) {
unordered_set<string> next;
// always consider the smaller set first
if (begin.size() > end.size()) {
swap(begin, end);
}
for (string s: begin) {
for (int i = 0; i < (int)s.length(); ++i) {
string changed = s;
for (char c = 'a'; c <= 'z'; ++c) {
if (c == s[i]) continue;
changed[i] = c;
// pre-emptively check
if (end.count(changed)) {
return 1 + level;
}
if (!visited.count(changed) and words.count(changed)) {
next.insert(changed);
visited.insert(changed);
}
}
}
}
++level;
// we have moved to the next level
begin = next;
}
return 0;
}Complexity Analysis
- Time Complexity: , where branching factor of the tree and distance of target from source.
- Space Complexity: (where size of
wordList), required forbegin,end, andvisitedhashsets.