题目来源:https://leetcode.com/problems/word-ladder-ii/
问题描述
126. Word Ladder II
Hard
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和终结词endWord,以及一个词表wordList(wordList包含endWord),求这样的最短路径,使得每次只修改一个字符,借助wordList可以从beginWord变换到endWord. 要求输出所有的最短路径。
------------------------------------------------------------
思路
bfs求无向无权图的最短路。
首先建图,节点是word,边表示word[i]和word[j]之间只需修改一个字符。图用邻接表存储。
然后bfs. 与一般的bfs不同,这里需要输出所有的最短路的路径,因此要用一个类Node(val, level)额外记下level信息,表示节点val需要从源走level步得到。要用pre数组记录每个节点的最短路径上的前驱节点列表(之所以是前驱节点列表而不是前驱节点是因为可能有多条最短路径)。由于要找到所有最短路径,所以对于图上的一个节点可能会多次访问,但这个一个节点的<val, level>对只能进入bfs队列一次,否则会造成后续的重复,因此还需要一个inq的map记录<val, level>对是否在bfs队列中。
------------------------------------------------------------
代码
class Solution {
public:
class Node {
public:
int val, level; // value, length from beginWord
Node(int _val, int _level): val(_val), level(_level) {}
bool operator < (const Node & other) const {
if (val < other.val) {
return true;
} else if (val > other.val) {
return false;
} else {
return level < other.level;
}
}
};
// check if $a and $b differs on only one letter
static bool differCnt(const string &a, const string &b) {
int cnt = 0, i = 0, n = a.length();
for (i=0; i<n; i++) {
if (a[i]!=b[i]) {
cnt++;
if (cnt > 1) {
return false;
}
}
}
return cnt == 1;
}
// dfs compute all shortest pathes as $ret using $pre
// $begin: index of begin word
// $cur: index of current word
// $path: current reversed path
static void dfs(vector<vector<string> > &ret, const vector<vector<Node> > &pre, const vector<string> &wordList, int begin, int cur, vector<string> &path) {
path.push_back(wordList[cur]);
if (cur == begin) {
vector<string> rpath(path);
reverse(rpath.begin(), rpath.end());
ret.push_back(rpath);
path.pop_back();
return;
}
for (auto item: pre[cur]) {
dfs(ret, pre, wordList, begin, item.val, path);
}
path.pop_back();
return;
}
// find index of endWord in wordList
// return -1 if not found
static int findEnd(string target, const vector<string> &wordList) {
int i = 0, n = wordList.size();
for (i=0; i<n; i++) {
if (target == wordList[i]) {
return i;
}
}
return -1;
}
vector<vector<string>> findLadders(string beginWord, string endWord, vector<string>& wordList) {
vector<vector<string> > ret;
int n = wordList.size();
if (n == 0) { // empty $wordList
return ret;
}
int eind = findEnd(endWord, wordList); // index of $endWord in $wordList
if (eind == -1) { // $endWord not found in $wordList
return ret;
}
// construct graph
// nodes: $n words from $wordList and $beginWord as $n+1
// edges: if node $i and $j differs on only one letter, than there exist edge $i=>$j and edge $j=>$i
wordList.push_back(beginWord);
vector<vector<int> > graph(n+1, vector<int>()); // adjacent list
int i = 0, j = 0;
for (i=0; i<n+1; i++) { // only go through the first half
for (j=i+1; j<n+1; j++) {
if (differCnt(wordList[i], wordList[j])) {
graph[i].push_back(j);
graph[j].push_back(i);
}
}
}
// bfs
int shortest = -2; // length of shortest path
queue<Node> q; // bfs queue
map<Node, bool> inq; // if node(val, level) is in bfs queue to drop duplicates
vector<vector<Node> > pre(n+1, vector<Node>()); // pre node in bfs
q.push(Node(n, 0)); // from beginWord
inq[Node(n, 0)] = true;
pre[n].push_back(Node(-1, -1));
while (!q.empty()) {
Node head = q.front();
if (head.level == shortest) {
break;
}
q.pop();
inq[head] = false;
for (auto item: graph[head.val]) {
if ((empty(pre[item]) || head.level == pre[item][0].level)) {
pre[item].push_back(head);
Node newNode(item, head.level+1);
if (!inq[newNode]) {
q.push(newNode);
inq[newNode] = true;
}
}
if (item == n+1) {
shortest = head.level+1;
break;
}
}
}
vector<string> path;
dfs(ret, pre, wordList, n, eind, path);
return ret;
}
};