LeetCode 126. Word Ladder II(bfs回溯路径)

该博客探讨了LeetCode的126题,即如何从`beginWord`通过逐步改变一个字母找到到达`endWord`的最短变换序列。题目要求每个中间词必须存在于给定的字典`wordList`中。作者使用广度优先搜索(BFS)来解决这个问题,并特别强调了如何在BFS过程中存储和输出所有最短路径的细节。给出了示例输入和输出,以及解题思路和代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目来源: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:

  1. Only one letter can be changed at a time
  2. 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;
    }
};

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值