Given two words (beginWord and endWord), and a dictionary's word list, find all shortest transformation sequence(s) 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 an empty list 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:
[
["hit","hot","dot","dog","cog"],
["hit","hot","lot","log","cog"]
]
Example 2:
Input:
beginWord = "hit"
endWord = "cog"
wordList = ["hot","dot","dog","lot","log"]
Output: []
Explanation: The endWord "cog" is not in wordList, therefore no possible transformation.
思路:
我们按照单词之间的距离生成一个树。该树的父子结点之间只有一个字母的不同,根节点是beginWord。因为生成树的方式不同,其遍历的时间也不同。这里提供两种生成树的方案,其中第一种直观简单,但是遍历时时间复杂度高,而且非常耗费空间,提交后超时;第二种没有超时。但是请注意,本质上这两种方式是一样的。不过第二种方法实现起来确实比较麻烦。这道题的接受率也算是比较低的。
1 简单粗暴版
以Example1 为例,我们生成的二叉树是这样的:
从图中我们可以看到,我们是一个一个的往下生成,但是层之间没有联系,这样会有很多冗余的空间,但是实现相对简单。
private:
struct TreeNode{
string val;
vector<TreeNode*> next;
TreeNode(string x) : val(x){}
};
public:
vector<string> changeOne(string s, vector<string>& list){
vector<string> res;
vector<string> left;
for (int i = 0; i < list.size(); i++){
int diffCount = 0;
for (int j = 0; j < s.size(); j++){
if (s[j] != list[i][j]) diffCount++;
}
if (diffCount == 1) res.push_back(list[i]);
else left.push_back(list[i]);
}
list = left;
return res;
}
TreeNode *genTree(string begin,vector<string> list){
TreeNode *root = new TreeNode(begin);
vector<string> nextList = changeOne(begin, list);
if (nextList.size() == 0) return root;
for (int i = 0; i < nextList.size(); i++){
root->next.push_back({});
root->next[i] = genTree(nextList[i], list);
}
return root;
}
void DFS(TreeNode *root, string end, vector<string> solution, vector<vector<string>>& res){
if (!root) return;
solution.push_back(root->val);
if (root->val == end){
res.push_back(solution);
return;
}
for (int i = 0; i < root->next.size(); i++){
DFS(root->next[i], end, solution, res);
}
}
vector<vector<string>> findLadders(string beginWord, string endWord, vector<string>& wordList) {
TreeNode *root = genTree(beginWord, wordList);
vector<vector<string>> res;
DFS(root, endWord, {}, res);
int minLen = INT_MAX;
for (int i = 0; i < res.size(); i++){
minLen = min(minLen, int(res[i].size()));
}
vector<vector<string>> temp;
for (int i = 0; i < res.size(); i++){
if (res[i].size() == minLen)
temp.push_back(res[i]);
}
return temp;
}
2 去冗余版
我们给出没有冗余的时候生成的树:
可以看到,层与层之间会通过他们的子节点有联系,这样会大大降低搜索时的时间复杂度并减少存储空间。
vector<TreeNode*> getNext(TreeNode* root, vector<TreeNode*> nodes){
vector<TreeNode*> nextNodes;
vector<TreeNode*> leftNodes;
for (int i = 0; i < nodes.size(); i++){
int diffCount = 0;
for (int j = 0; j < root->val.size(); j++){
if (root->val[j] != nodes[i]->val[j])
diffCount++;
}
if (diffCount == 1) nextNodes.push_back(nodes[i]);
else leftNodes.push_back(nodes[i]);
}
return nextNodes;
}
void genTree2(TreeNode *root, vector<TreeNode*> nodes){
vector<TreeNode*> record;
vector<TreeNode*> restNodes;
set<TreeNode*> usedNodes;
for (int i = 0; i < nodes.size(); i++)
if (root->val != nodes[i]->val)
usedNodes.insert(nodes[i]);
copy(usedNodes.begin(), usedNodes.end(), back_inserter(restNodes));
record.push_back(root);
TreeNode *last = root;
while (record.size() && restNodes.size()){
TreeNode *curr = record.front();
record.erase(record.begin());
vector<TreeNode*> nextNodes = getNext(curr, restNodes);
curr->next = nextNodes;
for (int i = 0; i < nextNodes.size(); i++){
if (find(record.begin(), record.end(), nextNodes[i]) == record.end())
record.push_back(nextNodes[i]);
usedNodes.erase(nextNodes[i]);
}
if (curr == last){
if (restNodes.size() == usedNodes.size()) return;
last = record.back();
restNodes = {};
copy(usedNodes.begin(), usedNodes.end(), back_inserter(restNodes));
}
}
}
void DFS2(TreeNode *root, string end, vector<string> solution, vector<vector<string>>& res, int layer, int depth){
layer++;
if (layer > depth) return;
if (!root) return;
solution.push_back(root->val);
if (root->val == end){
res.push_back(solution);
return;
}
for (int i = 0; i < root->next.size(); i++){
DFS2(root->next[i], end, solution, res, layer, depth);
}
}
vector<vector<string>> findLadders2(string beginWord, string endWord, vector<string>& wordList){
vector<TreeNode*> nodes;
for (int i = 0; i < wordList.size(); i++){
nodes.push_back(new TreeNode(wordList[i]));
}
TreeNode *root = new TreeNode(beginWord);
genTree2(root, nodes);
vector<vector<string>> res;
DFS2(root, endWord, {}, res, 0, nodes.size());
int minLen = INT_MAX;
for (int i = 0; i < res.size(); i++){
minLen = min(minLen, int(res[i].size()));
}
vector<vector<string>> temp;
for (int i = 0; i < res.size(); i++){
if (res[i].size() == minLen)
temp.push_back(res[i]);
}
return temp;
}
树节点的定义请参看第一份代码或完整实现。