Word Ladder II

hard graph bfs backtracking hash set string

Problem

Given two words beginWord and endWord and a word list, return all shortest transformation sequences from beginWord to endWord. Each adjacent pair differs by exactly one letter, and every intermediate word must be in the list.

InputbeginWord="hit", endWord="cog", wordList=["hot","dot","dog","lot","log","cog"]
Output[["hit","hot","dot","dog","cog"],["hit","hot","lot","log","cog"]]

from collections import defaultdict, deque
def find_ladders(begin, end, word_list):
    words = set(word_list)
    if end not in words: return []
    parents = defaultdict(set)
    level = {begin}
    found = False
    while level and not found:
        next_level = set()
        for w in level: words.discard(w)
        for w in level:
            for i in range(len(w)):
                for c in "abcdefghijklmnopqrstuvwxyz":
                    nw = w[:i] + c + w[i+1:]
                    if nw in words:
                        if nw == end: found = True
                        next_level.add(nw)
                        parents[nw].add(w)
        level = next_level
    if not found: return []
    result = []
    def backtrack(word, path):
        if word == begin:
            result.append([begin] + path[::-1])
            return
        for p in parents[word]:
            backtrack(p, path + [word])
    backtrack(end, [])
    return result
function findLadders(begin, end, wordList) {
  const words = new Set(wordList);
  if (!words.has(end)) return [];
  const parents = new Map();
  let level = new Set([begin]);
  let found = false;
  while (level.size && !found) {
    const next = new Set();
    for (const w of level) words.delete(w);
    for (const w of level) {
      for (let i = 0; i < w.length; i++) {
        for (let c = 97; c < 123; c++) {
          const nw = w.slice(0, i) + String.fromCharCode(c) + w.slice(i + 1);
          if (words.has(nw)) {
            if (nw === end) found = true;
            next.add(nw);
            if (!parents.has(nw)) parents.set(nw, new Set());
            parents.get(nw).add(w);
          }
        }
      }
    }
    level = next;
  }
  if (!found) return [];
  const result = [];
  function backtrack(word, path) {
    if (word === begin) { result.push([begin, ...path]); return; }
    for (const p of parents.get(word) || []) backtrack(p, [word, ...path]);
  }
  backtrack(end, []);
  return result;
}
class Solution {
    public List<List<String>> findLadders(String begin, String end, List<String> wordList) {
        Set<String> words = new HashSet<>(wordList);
        if (!words.contains(end)) return new ArrayList<>();
        Map<String, Set<String>> parents = new HashMap<>();
        Set<String> level = new HashSet<>(); level.add(begin);
        boolean found = false;
        while (!level.isEmpty() && !found) {
            Set<String> next = new HashSet<>();
            words.removeAll(level);
            for (String w : level) {
                char[] cs = w.toCharArray();
                for (int i = 0; i < cs.length; i++) {
                    char orig = cs[i];
                    for (char c = 'a'; c <= 'z'; c++) {
                        cs[i] = c;
                        String nw = new String(cs);
                        if (words.contains(nw)) {
                            if (nw.equals(end)) found = true;
                            next.add(nw);
                            parents.computeIfAbsent(nw, k -> new HashSet<>()).add(w);
                        }
                    }
                    cs[i] = orig;
                }
            }
            level = next;
        }
        List<List<String>> result = new ArrayList<>();
        if (found) backtrack(end, begin, new ArrayList<>(), result, parents);
        return result;
    }
    void backtrack(String w, String begin, List<String> path, List<List<String>> out, Map<String, Set<String>> parents) {
        path.add(0, w);
        if (w.equals(begin)) { out.add(new ArrayList<>(path)); }
        else for (String p : parents.getOrDefault(w, new HashSet<>())) backtrack(p, begin, path, out, parents);
        path.remove(0);
    }
}
vector<vector<string>> findLadders(string begin, string end, vector<string>& wordList) {
    unordered_set<string> words(wordList.begin(), wordList.end());
    if (!words.count(end)) return {};
    unordered_map<string, vector<string>> parents;
    unordered_set<string> level{begin};
    bool found = false;
    while (!level.empty() && !found) {
        unordered_set<string> next;
        for (auto& w : level) words.erase(w);
        for (auto& w : level) {
            string nw = w;
            for (int i = 0; i < (int)w.size(); i++) {
                char orig = nw[i];
                for (char c = 'a'; c <= 'z'; c++) {
                    nw[i] = c;
                    if (words.count(nw)) {
                        if (nw == end) found = true;
                        next.insert(nw);
                        parents[nw].push_back(w);
                    }
                }
                nw[i] = orig;
            }
        }
        level = next;
    }
    vector<vector<string>> result;
    if (!found) return result;
    function<void(string, vector<string>&)> bt = [&](string w, vector<string>& path) {
        path.push_back(w);
        if (w == begin) { vector<string> v(path.rbegin(), path.rend()); result.push_back(v); }
        else for (auto& p : parents[w]) bt(p, path);
        path.pop_back();
    };
    vector<string> path;
    bt(end, path);
    return result;
}
Time: O(N · L²) where N = |wordList|, L = word length Space: O(N · L) for graph + output