126. Word Ladder II

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.

Rust Solution

struct Solution;

use std::collections::HashMap;
use std::collections::HashSet;
use std::iter::FromIterator;

impl Solution {
    fn find_ladders(
        begin_word: String,
        end_word: String,
        word_list: Vec<String>,
    ) -> Vec<Vec<String>> {
        let mut dict: HashSet<String> = HashSet::new();
        for word in word_list {
            dict.insert(word);
        }
        if !dict.contains(&end_word) {
            return vec![];
        }
        let set1: HashSet<String> = HashSet::from_iter(vec![begin_word.to_string()]);
        let set2: HashSet<String> = HashSet::from_iter(vec![end_word.to_string()]);
        let mut map: HashMap<String, HashSet<String>> = HashMap::new();
        Self::bfs(set1, set2, false, &mut map, &mut dict);
        let mut path = vec![begin_word.to_string()];
        let mut res = vec![];
        Self::dfs(&begin_word, &mut path, &mut res, &map, &end_word);
        res
    }

    fn bfs(
        set1: HashSet<String>,
        set2: HashSet<String>,
        flipped: bool,
        map: &mut HashMap<String, HashSet<String>>,
        dict: &mut HashSet<String>,
    ) {
        if set1.is_empty() {
            return;
        }
        if set1.len() > set2.len() {
            Self::bfs(set2, set1, !flipped, map, dict);
            return;
        }
        for s in set1.iter() {
            dict.remove(s);
        }
        for s in set2.iter() {
            dict.remove(s);
        }
        let mut next: HashSet<String> = HashSet::new();
        let mut done = false;
        for word in set1.iter() {
            for next_word in Self::connected_words(&word.to_string()) {
                let key = if flipped {
                    next_word.to_string()
                } else {
                    word.to_string()
                };
                let value = if flipped {
                    word.to_string()
                } else {
                    next_word.to_string()
                };
                if set2.contains(&next_word) {
                    done = true;
                    map.entry(key).or_default().insert(value);
                } else if dict.contains(&next_word) {
                    next.insert(next_word);
                    map.entry(key).or_default().insert(value);
                }
            }
        }
        if !done {
            Self::bfs(set2, next, !flipped, map, dict);
        }
    }

    fn dfs(
        start: &str,
        path: &mut Vec<String>,
        all: &mut Vec<Vec<String>>,
        map: &HashMap<String, HashSet<String>>,
        end: &str,
    ) {
        if start == end {
            all.push(path.to_vec());
        } else {
            if let Some(nei) = map.get(start) {
                for next in nei.iter() {
                    path.push(next.to_string());
                    Self::dfs(next, path, all, map, end);
                    path.pop();
                }
            }
        }
    }

    fn connected_words(word: &str) -> Vec<String> {
        let n = word.len();
        let mut res = vec![];
        for i in 0..n {
            let mut s: Vec<char> = word.chars().collect();
            for j in 0..26 {
                let c = (b'a' + j as u8) as char;
                s[i] = c;
                let new_word: String = s.iter().collect();
                res.push(new_word);
            }
        }
        res
    }
}

#[test]
fn test() {
    let begin_word = "hit".to_string();
    let end_word = "cog".to_string();
    let word_list = vec_string!["hot", "dot", "dog", "lot", "log", "cog"];
    let mut res = vec_vec_string![
        ["hit", "hot", "dot", "dog", "cog"],
        ["hit", "hot", "lot", "log", "cog"]
    ];
    let mut ans = Solution::find_ladders(begin_word, end_word, word_list);
    res.sort();
    ans.sort();
    assert_eq!(ans, res);

    let begin_word = "hit".to_string();
    let end_word = "cog".to_string();
    let word_list = vec_string!["hot", "dot", "dog", "lot", "log"];
    let res = vec_vec_string![];
    assert_eq!(Solution::find_ladders(begin_word, end_word, word_list), res);

    let begin_word = "a".to_string();
    let end_word = "c".to_string();
    let word_list = vec_string!["a", "b", "c"];
    let mut res = vec_vec_string![["a", "c"]];
    let mut ans = Solution::find_ladders(begin_word, end_word, word_list);
    res.sort();
    ans.sort();
    assert_eq!(ans, res);
}

Having problems with this solution? Click here to submit an issue on github.