https://leetcode.com/problems/word-ladder/
Given two words (beginWord and endWord), and a dictionary's word list, find the length of 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. Note that beginWord is not a transformed word.
Note:
- Return 0 if there is no such transformation sequence.
- All words have the same length.
- All words contain only lowercase alphabetic characters.
- You may assume no duplicates in the word list.
- You may assume beginWord and endWord are non-empty and are not the same.
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.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.
我最开始用的最普通的广搜,使用map实现每个串和他的位置的映射,同时记录到达每一个串的步数,用于判断是否成环以及用于记录步数递增
class Solution {
public:
int ladderLength(string beginWord, string endWord, vector<string>& wordList) {
static int fast_io = []() { std::ios::sync_with_stdio(false); cin.tie(nullptr); return 0; }();
unordered_map<string, int> M;
vector<int> step(wordList.size()+1, -1);
queue<string> Q;
string cur, tem;
for(int i = 0; i < wordList.size(); ++i){
M[wordList[i]] = i;
}
if(M.find(endWord) == M.end()) return 0;
if(M.find(beginWord) == M.end()){
M[beginWord] = wordList.size();
step[wordList.size()] = 1;
}else{
step[M[beginWord]] = 1;
}
Q.push(beginWord);
while(!Q.empty()){
cur = Q.front(); Q.pop();
for(int i = 0; i < cur.size(); ++i){
for(char c = 'a'; c <= 'z'; ++c){
tem = cur;
tem[i] = c;
if(tem == endWord) return step[M[cur]]+1;
if(M.find(tem) != M.end() && step[M[tem]] == -1){
step[M[tem]] = step[M[cur]]+1;
Q.push(tem);
}
}
}
}
return 0;
}
};
但是其中有很多冗余,比如成环的情况可以通过每一次直接从wordlist中删除单词来避免,步数的记录可以通过每次将同一个level的单词从队列中除去后累加int量来实现,可以减少很多map查询的操作
更好的优化是使用两个集合,从前后两端开始搜索(因为beginword和endword从本质上是平等的)。将两者中较短的那个作为当前的活跃集合,因为活跃集合中的每个串的每个位置会被替换成26个字符,并有大量的set.find操作,因此使用较短的那个来执行可以节省很多操作。(直观上这个ladder的汇聚也是梭形的)
class Solution {
public:
int ladderLength(string beginWord, string endWord, vector<string>& wordList) {
static int fast_io = []() { std::ios::sync_with_stdio(false); cin.tie(nullptr); return 0; }();
unordered_set<string> words(wordList.begin(), wordList.end());
unordered_set<string> forward, backward, nextlevel;
if(words.find(endWord) == words.end()) return 0;
forward.insert(beginWord);
backward.insert(endWord);
int step = 2;
while(!forward.empty()){
for(auto cur : forward){
string tem = cur;
for(int i = 0; i < cur.size(); ++i){
for(int j = 0; j < 26; ++j){
tem[i] = 'a'+j;
if(backward.find(tem) != backward.end()) return step;
unordered_set<string>::iterator itr = words.find(tem);
if(itr != words.end()){
nextlevel.insert(tem);
words.erase(itr);
}
}
tem[i] = cur[i];
}
}
if(nextlevel.size() <= backward.size()){
forward = nextlevel;
nextlevel.clear();
}else{
forward = backward;
backward = nextlevel;
nextlevel.clear();
}
++step;
}
return 0;
}
};