【LeetCode】126. Word Ladder II
2016-09-07 18:43
387 查看
题目链接:
https://leetcode.com/problems/word-ladder-ii/
题目描述:
Given two words (beginWord and endWord), and a dictionary's word list, find all shortest transformation sequence(s) from beginWord toendWord, such that:
Only one letter can be changed at a time
Each intermediate word must exist in the word list
For example,
Given:
beginWord =
endWord =
wordList =
Return
词阶是一个单词序列。在词阶中,所有单词的长度相等,相邻的单词只相差一个字母,而且这个序列长度最短。
求出beginWord到endWord的所有词阶。
涉及内容:
BFS(广度优先搜索),找出两点之间所有的最短路径
思考:
这是一个关于图搜索的问题。把每个单词当作一个节点,可以把相差一个字母的两个单词对应的节点连接起来,然后找出beginWord到endWord所有最短路径。
我们需要求最短路径,而且这个图是无向无权图,可以想到使用广度优先搜索算法(BFS)
一种方法是输入beginWord和endWord后,对单词逐位修改,并判断修改后的单词是否在词典中,进行广度优先搜索。
另一种方法是首先根据单词之间的关系,建立出对应的无向无权图,再在图中搜索。
这里谈论第二种方法。
如何快速地建立图呢?
得到一个单词的邻居,最简单的方法是逐位修改,判断修改后的单词是否存在,若存在,则它们之前存在一条边。
我想到了一种方法,如data和date,它们的最后一位是不相同的。除去最后一位后,剩下的都是dat.
为此可以这样储存:
当建立图后,如何对图进行搜索,找出给定始末两点对应的所有最短路径呢?
首先思考如何得到一条最短路径
由于要搜索无权图的最短路径,可以想到使用BFS(广度优先搜索算法)
使用BFS,可以从出发点开始,由近到远搜索。
当检测到目标点时,需要回溯得到路径上的所有节点。
回溯的实现,可以使用两种方法:
方法1: 记录访问到的节点的上一个节点,回溯时延着上一个节点走即可。
方法2:在BFS的队列中加入一个栈结构,记录出发点到当前节点经过的所有节点,效率低,不推荐。
知道找一条最短路径的方法后,我们思考如何找到所有最短路径。
仿照回溯过程的方法1,可以在一个节点中,记录能一步到达这个节点的所有节点,如:
1 -> 2, 3 -> 2, 那么节点2记录{1, 3}
然后使用递归函数从目标节点到起点回溯,得到所有的最短路径。
参考代码:
https://leetcode.com/problems/word-ladder-ii/
题目描述:
Given two words (beginWord and endWord), and a dictionary's word list, find all shortest transformation sequence(s) from beginWord toendWord, such that:
Only one letter can be changed at a time
Each intermediate word must exist in the word list
For example,
Given:
beginWord =
"hit"
endWord =
"cog"
wordList =
["hot","dot","dog","lot","log"]
Return
[ ["hit","hot","dot","dog","cog"], ["hit","hot","lot","log","cog"] ]
词阶是一个单词序列。在词阶中,所有单词的长度相等,相邻的单词只相差一个字母,而且这个序列长度最短。
求出beginWord到endWord的所有词阶。
涉及内容:
BFS(广度优先搜索),找出两点之间所有的最短路径
思考:
这是一个关于图搜索的问题。把每个单词当作一个节点,可以把相差一个字母的两个单词对应的节点连接起来,然后找出beginWord到endWord所有最短路径。
我们需要求最短路径,而且这个图是无向无权图,可以想到使用广度优先搜索算法(BFS)
一种方法是输入beginWord和endWord后,对单词逐位修改,并判断修改后的单词是否在词典中,进行广度优先搜索。
另一种方法是首先根据单词之间的关系,建立出对应的无向无权图,再在图中搜索。
这里谈论第二种方法。
如何快速地建立图呢?
得到一个单词的邻居,最简单的方法是逐位修改,判断修改后的单词是否存在,若存在,则它们之前存在一条边。
我想到了一种方法,如data和date,它们的最后一位是不相同的。除去最后一位后,剩下的都是dat.
为此可以这样储存:
边记录 str_edges[int 修改位编号][string 删掉修改位后的字符串] = [修改位的字符] 如data有: str_edges[0]["ata"] = ['d',...] str_edges[1]["dta"] = ['a',...] str_edges[2]["dae"] = ['t',...] str_edges[3]["dat"] = ['a',...] 若要获得单词data的邻居,则获取str_edges[0]["ata"], str_edges[1]["dta"], str_edges[2]["dae"], str_edges[3]["dat"]的值 获取邻居的时间复杂度为:nlog(n)
当建立图后,如何对图进行搜索,找出给定始末两点对应的所有最短路径呢?
首先思考如何得到一条最短路径
由于要搜索无权图的最短路径,可以想到使用BFS(广度优先搜索算法)
使用BFS,可以从出发点开始,由近到远搜索。
当检测到目标点时,需要回溯得到路径上的所有节点。
回溯的实现,可以使用两种方法:
方法1: 记录访问到的节点的上一个节点,回溯时延着上一个节点走即可。
方法2:在BFS的队列中加入一个栈结构,记录出发点到当前节点经过的所有节点,效率低,不推荐。
知道找一条最短路径的方法后,我们思考如何找到所有最短路径。
仿照回溯过程的方法1,可以在一个节点中,记录能一步到达这个节点的所有节点,如:
1 -> 2, 3 -> 2, 那么节点2记录{1, 3}
然后使用递归函数从目标节点到起点回溯,得到所有的最短路径。
参考代码:
string to_lower(string str){ string res; for (int i = 0;i < str.size(); ++i){ char c = str[i]; if (c >= 'A' && c <= 'Z')c = c - 'A' + 'a'; res += c; } return res; } template <typename T> void Shuffle(vector<T> &vs){ for (int i = 0;i < vs.size(); ++i){ int r = rand() % vs.size(); swap(vs[i], vs[r]); } } template <typename T> T gmin(T a, T b){ return a<b?a:b; } //处理所有长度为n的单词 class WordsNetwork{ public: WordsNetwork(){} //传入长度为n的单词组 WordsNetwork(vector<string> &vs){ //构建网络 BuildNetwork(vs); } //传入长度为n的两个单词,返回路径; 若返回值为空, 则路径不存在 //使用算法, BFS 广度优先搜索 vector<string> GetPath(string from, string to){ if (from.size() != len || to.size() != len)return vector<string>(); // 不合法 if (from == to)return vector<string>(1, from); // 对于同一节点 if (!sid.count(from) || !sid.count(to))return vector<string>(); // 不存在其中一个单词 vector<int> rec(sdata.size()); vector<bool> vis(sdata.size(), false); int fid = sid[from]; int tid = sid[to]; queue<int> q; // 队列元素 q.push(fid); rec[fid] = -1; vis[fid] = true; while(!q.empty()){ int id = q.front(); q.pop(); if (id == tid){ //到达目标点 stack<string> st; while(id != -1){ st.push(sdata[id]); id = rec[id]; // 取上一个节点 } vector<string> path; while(!st.empty()){ path.push_back(st.top()); st.pop(); } return path; } // 没有达到目标点,添加邻居 for (int i = 0;i < edges[id].size(); ++i){ int nid = edges[id][i]; if (vis[nid])continue; vis[nid] = true; rec[nid] = id; q.push(nid); } } return vector<string>(); } vector<vector<string> > GetPaths(string from, string to){ if (from.size() != len || to.size() != len)return vector<vector<string> >(); // 不合法 if (from == to)return vector<vector<string> >(1, vector<string>(1, from)); // 对于同一节点 if (!sid.count(from) || !sid.count(to))return vector<vector<string> >(); // 不存在其中一个单词 vector<bool> vis(sdata.size(), false); vector<int> deep(sdata.size()); vector<set<int> > lastNode(sdata.size()); int fid = sid[from]; int tid = sid[to]; queue<pair<int, int> > q; q.push(make_pair(fid, 0)); vis[fid] = true; while(!q.empty()){ pair<int, int> p = q.front(); q.pop(); int id = p.first; int distance = p.second; deep[id] = distance; if (id == tid){ continue; } // 没有达到目标点,添加邻居 for (int i = 0;i < edges[id].size(); ++i){ int nid = edges[id][i]; if (!vis[nid] || deep[nid] == distance + 1){ //id -> nid 正好距离为1 lastNode[nid].insert(id); deep[nid] = distance + 1; } if (vis[nid])continue; vis[nid] = true; q.push(make_pair(nid, distance + 1)); } } //生成路径 vector<vector<string> > paths; if (!lastNode[tid].empty()){ stack<int> st; st.push(tid); GeneratePaths(paths, lastNode, tid, st); } return paths; } void GeneratePaths(vector<vector<string> > &paths, const vector<set<int> > &lastNode, int tid, stack<int> st){ cout << tid << endl; if (lastNode[tid].empty()){ //走到开头 vector<string> path; while(!st.empty()){ path.push_back(sdata[st.top()]); st.pop(); } paths.push_back(path); }else{ for (set<int>::const_iterator iter = lastNode[tid].begin(); iter != lastNode[tid].end(); ++iter){ cout << tid << "==" << *iter << endl; stack<int> y = st; y.push(*iter); GeneratePaths(paths, lastNode, *iter, y); } } } private: void BuildNetwork(vector<string> &vs){ //若vs为空 if (vs.empty())return; //记下字符串 sdata = vs; //由于传入单词组的长度一致,因此取第一个即可 len = vs[0].size(); str_edges.resize(len); for (int i = 0;i < vs.size(); ++i){ string &word = vs[i]; sid[word] = i; // 记录id for (int j = 0;j < len;++j){ string cut; // 删除第j位单词后的结果 for (int k = 0;k < len;++k){ if (j == k)continue; cut += word[k]; } str_edges[j][cut].insert(word[j]); } } //建立id边 set<pair<int, int> > vis; // 已经添加边 edges.resize(vs.size()); for (int i = 0;i < vs.size(); ++i){ string &word = vs[i]; int fid = sid[word]; for (int j = 0;j < len; ++j){ //改变第j位 string s; for (int k = 0;k < len; ++k){ if (j != k)s += word[k]; } set<char> &se = str_edges[j][s]; //构造邻居 for (set<char>::iterator iter = se.begin(); iter != se.end(); ++iter){ if (*iter == word[j])continue; string neibor = word; neibor[j] = *iter; // 改变字母 int tid = sid[neibor]; //fid -> tid if (vis.count(make_pair(fid, tid)) || vis.count(make_pair(tid, fid)))continue; // 已添加 vis.insert(make_pair(fid, tid)); // 记录 edges[fid].push_back(tid); edges[tid].push_back(fid); } } } } private: int len; /* 边记录 str_edges[int 修改位编号][string 删掉修改位后的字符串] = [修改位的字符] 如data有: str_edges[0]["ata"] = ['d',...] str_edges[1]["dta"] = ['a',...] str_edges[2]["dae"] = ['t',...] str_edges[3]["dat"] = ['a',...] 若要获得单词data的邻居,则获取str_edges[0]["ata"], str_edges[1]["dta"], str_edges[2]["dae"], str_edges[3]["dat"]的值 获取邻居的时间复杂度为:nlog(n) */ vector<map<string, set<char> > > str_edges; map<string, int> sid; // 从string映射到id vector<vector<int> > edges; // 边 vector<string> sdata; }; class Solution { public: vector<vector<string>> findLadders(string beginWord, string endWord, unordered_set<string> &wordList) { vector<string> vs; for (auto w:wordList)vs.push_back(w); WordsNetwork ns(vs); return ns.GetPaths(beginWord, endWord); } };
相关文章推荐
- Leetcode 126. Word Ladder II[hard]
- LeetCode 126 Word Ladder II
- Leetcode 127. Word Ladder I & 126. Word Ladder II
- leetcode 126. Word Ladder II
- LeetCode 126. Word Ladder II(单词梯子)
- Leetcode 126. Word Ladder II
- [Leetcode 12] 126 Word Ladder II TO_BE_ADDED
- [leetcode] 126 Word Ladder ii bfs dfs
- Leetcode 126. Word Ladder II
- [LeetCode] 126. Word Ladder II 词语阶梯 II
- leetcode[126]Word Ladder II
- leetcode_c++:Word Ladder II(126)
- LeetCode[126.Word Ladder II]题解 难度[hard]
- Leetcode 126. Word Ladder II 字符变换2 解题报告
- [LeetCode]题解(python):126-Word Ladder II
- [LeetCode] 126. Word Ladder II
- Leetcode 126. Word Ladder II
- [leetcode 126] Word Ladder II
- [leetcode]126. Word LadderII@Java解题报告
- LeetCode力扣之126. Word Ladder II