给出两个单词(start和end)与一个字典,找出从start到end的最短转换序列。规则如下:
- 一次只能改变一个字母
- 中间单词必须在字典里存在
例如:
给出
start = "hit"
end = "cog"
dict = ["hot","dot","dog","lot","log"]
返回
[
["hit","hot","dot","dog","cog"],
["hit","hot","lot","log","cog"]
]
注意
- 所有单词的长度一样
- 所有单词中只有小写字母
初始思路
最直接的想法是从start开始,对每个字母位置从'a'到'z'尝试替换。如果替换字母后的单词在字典中,将其加入路径,然后以新单词为起点进行递归调用,否则继续循环。每层递归函数终止的条件是end出现或者单词长度*26次循环完毕。end出现时表示找到一个序列,对比当前最短序列做相应更新即可。
处理过程中需要注意的主要有几点:
- 不能相同字母替换,如hot第一个位置遍历到h时不应该处理。否则就会不断的在hot上死循环。因此在替换后前要做一次检查。
- 我们要找的是最短的转换方案,所以转换序列不应该出现重复的单词。否则组合将会有无数多种,如例子中的["hit","hot","dot","dog","dot","dog","dog",....."cog"]。这里我们可以使用一个unordered_set容器来保存某次一次替换序列中已出现过的单词,也可以每次使用std:find去搜索当前替换序列。如果使用unordered_set,在递归处理时,和单词序列一样,要在递归后做相应的出栈操作。
- 处理过程中如果发现当前处理序列长度已经超过当前最短序列长度,可以中止对该序列的处理,因为我们要找的是最短的序列。
1 class Solution { 2 public: 3 std::vector<std::vector<std::string>> findLadders(std::string start, std::string end, std::unordered_set<std::string> &dict) 4 { 5 std::vector<std::vector<std::string>> result; 6 std::vector<std::string> entry; 7 8 entry.push_back(start); 9 Find(start, end, dict, 0, result, entry); 10 11 return result; 12 } 13 14 private: 15 void Find(std::string& start, const std::string& end, const std::unordered_set<std::string> &dict 16 , size_t positionToChange, std::vector<std::vector<std::string>>& result, std::vector<std::string>& entry) 17 { 18 //如果长度已经等于当前结果中的长度,再找出来肯定就 19 //超过了,终止处理 20 if(!result.empty() && entry.size() == result[0].size()) 21 { 22 return; 23 } 24 25 for(size_t pos = positionToChange; pos < start.size(); ++pos) 26 { 27 char beforeChange = ' '; 28 for(int i = 'a'; i <= 'z'; ++i) 29 { 30 //防止同字母替换 31 if(start[pos] == i) 32 { 33 continue; 34 } 35 beforeChange = start[pos]; 36 start[pos] = i; 37 38 //用std::find的话 39 /* 40 if(std::find(entry.begin(), entry.end(), start) != entry.end()) 41 { 42 start[pos] = beforeChange; 43 continue; 44 } 45 */ 46 //如果单词已经用过的情况 47 if(!used_.empty() && used_.count(start)!=0 ) 48 { 49 start[pos] = beforeChange; 50 continue; 51 } 52 53 54 if(start == end) 55 { 56 entry.push_back(start); 57 58 //只需要保存最短的序列 59 if(!result.empty()) 60 { 61 if(entry.size() < result[0].size()) 62 { 63 result.clear(); 64 result.push_back(entry); 65 } 66 else if(entry.size() == result[0].size()) 67 { 68 result.push_back(entry); 69 } 70 } 71 else 72 { 73 result.push_back(entry); 74 } 75 //完成一个序列,把前面加入的end弹出 76 entry.pop_back(); 77 return; 78 } 79 80 if(dict.find(start) != dict.end()) 81 { 82 entry.push_back(start); 83 used_.insert(start); 84 Find(start, end, dict, 0, result, entry); 85 used_.erase(*entry.rbegin()); 86 entry.pop_back(); 87 88 89 if(!entry.empty()) 90 { 91 start = *entry.rbegin(); 92 } 93 else 94 { 95 start[pos] = beforeChange; 96 } 97 } 98 else 99 { 100 start[pos] = beforeChange; 101 } 102 } 103 } 104 105 return; 106 } 107 108 std::unordered_set<std::string> used_; 109 };