LeetCode-Word Ladder II-单词梯-BFS搜索构图+DFS
https://oj.leetcode.com/problems/word-ladder-ii/
首先,要打印出所有路径,就必须用到DFS+pre的技术。因此需要构造出这个图,又因为需要打印出最短距离,所以构造图的同时还需要知道到终点的最短路径。
构造图如果枚举所有点对会超时,O(n^2)算法不可行。
考虑使用BFS搜索构造足够打印所有最短路径的图。从start节点出发,枚举他可能变化成的所有词典单词,BFS拓展节点。
需要注意的事项有两个:
1)必须要保证队列中不能出现过以前出现过的单词,因为这样会使图特别大,并且从以前加入队列的单词再出发BFS不会使得路径更短。
2)必须要保证以按层次搜索的角度来看,任一节点v上一层的所有前继节点到v的边都被构造在图中。
研究了很久发现,用一个levels[]来记录每个节点的深度值可以做到这一点。这其中对BFS的修改有三处:
1)初始化levels[]为INF,并时刻判断一个出队列节点的深度是否大于等于end的深度,如果大于等于,就不展开该节点,这样保证图不会构造的太大。
2)保证队列单词不会重复,levels[v]!=INF时,不要加入队列。
3)u是出队节点,保证levels[v]>levels[u]时有一条u->v的边。
构造出这个图之后,再进行一次DFS,这时需要注意一点可能这个图中存在长于到end最短路径(之前并没有及时停止,多加入了下层的一些节点)。所以需要保证DFS深度不超过end节点的level。
使用pre在DFS过程记录每个节点的前驱节点,当遇到end时,反向遍历到start并将这个路径加入结果即可。
代码如下:
typedef unordered_set<string> scset; typedef unordered_set<string>::iterator sciter; typedef pair<int,int> scpair; const int INF=9999; class Solution { public: int m,n; vector<vector<int>> g; unordered_map <string,int> cm; vector <string> elems; vector<vector<string>> tot; string end; vector <int> pres; vector <bool> vis; int minL; vector <int> levels; void DFS(int u,int l){ if (l>minL){return;} if (elems[u]==elems[1]){ tot.push_back(vector<string>()); vector <string> &cur=tot[tot.size()-1]; for (int i=u;i!=-1;i=pres[i]){ cur.push_back(elems[i]); } reverse(cur.begin(),cur.end()); return; } for (int i=0;i<g[u].size();i++){ int v=g[u][i]; pres[v]=u; DFS(v,l+1); } } vector<vector<string>> findLadders(string start, string end, unordered_set<string> &dict) { m=start.length(); sciter it=dict.find(start); if (it!=dict.end()){dict.erase(it);} it=dict.find(end); if (it!=dict.end()){dict.erase(it);} n=dict.size()+2; g.resize(n,vector<int>()); elems.resize(n); elems[0]=start; elems[1]=end; cm[start]=0; cm[end]=1; int count=2; for (sciter it=dict.begin();it!=dict.end();it++){ elems[count]=*it; cm[*it]=count; count++; } list <int> que; que.push_back(0); dict.insert(end); levels.resize(n,INF); levels[0]=0; while(!que.empty()){ int u=que.front(); que.pop_front(); string us=elems[u]; if (levels[u]>=levels[1]){continue;} //not longer, we just need the shortest path for (int i=0;i<m;i++){ //all possible changes from cur node for (int j=0;j<26;j++){ string us=elems[u]; char nc=j+'a'; if (nc==us[i]){continue;} string vs=us; vs[i]=nc; sciter it=dict.find(vs); if (it!=dict.end()){ //we have a edge from u->v int v=cm[vs]; if (levels[v]>levels[u]){ //ensure a DAG is constructed g[u].push_back(v); } if (levels[v]!=INF){ //ensure not repeat continue; } levels[v]=levels[u]+1; //node's not repeat, the level is unique for every word que.push_back(v); } } } } minL=levels[1]; vis.assign(n,false); pres.resize(n,-1); DFS(0,0); return tot; } };