Shortest Transformation Sequences in a Word List

Last Updated : 4 Jul, 2026

Given two distinct words s and e, and a list of unique words words[], where all words have the same length, find all shortest transformation sequences from s to e. A valid transformation sequence must satisfy the following conditions:

  • Only one character can be changed in each transformation.
  • Every transformed word must exist in words[], including e.
  • All words consist only of lowercase English letters.
  • s may or may not be present in words[].

Return all shortest transformation sequences from s to e. If no such sequence exists, return an empty list. The sequences may be returned in any order.

Examples: 

Input: s = "der", e = "dfs", words[] = ["des", "der", "dfr", "dgt", "dfs"]
Output: [["der", "des", "dfs"], ["der", "dfr", "dfs"]]
Explanation: There are two shortest transformation sequences from "der" to "dfs", each having a length of 3:
"der" -> "dfr" -> "dfs"
"der" -> "des" -> "dfs"
Each transformation changes exactly one character, and every intermediate word belongs to words[].

Input: s = "gedk", e = "geek", words[] = ["geek", "gefk"]
Output: [["gedk", "geek"]]
Explanation: The word "gedk" can be transformed directly into "geek" by changing the third character ['d' to 'e']. Since "geek" is present in words[], the shortest transformation sequence is:
"gedk" -> "geek"

Try It Yourself
redirect icon

[Naive Approach] BFS by Storing Complete Paths - O(k * n * m) Time and O(k * n * m) Space

The idea is to perform BFS while storing the complete transformation sequence in the queue. For each word, all valid one-character transformations are generated and appended to the current sequence. Since BFS processes words level by level, the first sequences reaching the target word are the shortest, and all such sequences are returned.

C++
#include <vector>
#include <unordered_set>
#include <queue>
#include <string>
#include <iostream>

using namespace std;

vector<vector<string>> findSequences(vector<string> &words, string &s, string &e)
{
    unordered_set<string> st(words.begin(), words.end());

    // Queue stores complete transformation sequences.
    queue<vector<string>> q;
    q.push({s});

    // Stores words used at the current BFS level.
    vector<string> usedOnLevel;
    usedOnLevel.push_back(s);

    int level = 0;
    vector<vector<string>> ans;

    while (!q.empty())
    {
        vector<string> path = q.front();
        q.pop();

        // Remove words used in the previous level.
        if ((int)path.size() > level)
        {
            level = path.size();

            for (string &word : usedOnLevel)
                st.erase(word);

            usedOnLevel.clear();
        }

        string word = path.back();

        // If target word is reached, store the sequence.
        if (word == e)
        {
            if (ans.empty())
                ans.push_back(path);

            else if (ans[0].size() == path.size())
                ans.push_back(path);
        }

        // Generate all possible one-character transformations.
        for (int i = 0; i < word.size(); i++)
        {
            string temp = word;

            for (char ch = 'a'; ch <= 'z'; ch++)
            {
                temp[i] = ch;

                // Valid transformed word found.
                if (st.count(temp))
                {
                    path.push_back(temp);
                    q.push(path);

                    usedOnLevel.push_back(temp);

                    path.pop_back();
                }
            }
        }
    }

    return ans;
}

int main()
{
    string s = "der";
    string e = "dfs";

    vector<string> words = {"des", "der", "dfr", "dgt", "dfs"};

    vector<vector<string>> ans = findSequences(words, s, e);

    cout << "[";

    for (int i = 0; i < ans.size(); i++)
    {
        cout << "[";

        for (int j = 0; j < ans[i].size(); j++)
        {
            cout << '"' << ans[i][j] << '"';

            if (j + 1 < ans[i].size())
                cout << ", ";
        }

        cout << "]";

        if (i + 1 < ans.size())
            cout << ", ";
    }

    cout << "]";

    return 0;
}
Java
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Queue;

class GFG {

    public static ArrayList<ArrayList<String>>
    findSequences(String s, String e, String[] words)
    {

        HashSet<String> st
            = new HashSet<>(Arrays.asList(words));

        // Queue stores complete transformation sequences.
        Queue<ArrayList<String>> q = new LinkedList<>();

        ArrayList<String> startPath = new ArrayList<>();
        startPath.add(s);
        q.offer(startPath);

        // Stores words used at the current BFS level.
        ArrayList<String> usedOnLevel = new ArrayList<>();
        usedOnLevel.add(s);

        int level = 0;
        ArrayList<ArrayList<String>> ans
            = new ArrayList<>();

        while (!q.isEmpty()) {

            ArrayList<String> path = q.poll();

            // Remove words used in the previous level.
            if (path.size() > level) {
                level = path.size();

                for (String word : usedOnLevel) {
                    st.remove(word);
                }

                usedOnLevel.clear();
            }

            String word = path.get(path.size() - 1);

            // If target word is reached, store the
            // sequence.
            if (word.equals(e)) {

                if (ans.isEmpty()) {
                    ans.add(new ArrayList<>(path));
                }
                else if (ans.get(0).size() == path.size()) {
                    ans.add(new ArrayList<>(path));
                }
            }

            // Generate all possible one-character
            // transformations.
            for (int i = 0; i < word.length(); i++) {

                char[] temp = word.toCharArray();

                for (char ch = 'a'; ch <= 'z'; ch++) {

                    temp[i] = ch;
                    String nextWord = new String(temp);

                    // Valid transformed word found.
                    if (st.contains(nextWord)) {

                        path.add(nextWord);
                        q.offer(new ArrayList<>(path));

                        usedOnLevel.add(nextWord);

                        path.remove(path.size() - 1);
                    }
                }
            }
        }

        return ans;
    }

    public static void main(String[] args)
    {
        String s = "der";
        String e = "dfs";

        String[] words
            = { "des", "der", "dfr", "dgt", "dfs" };

        ArrayList<ArrayList<String>> ans
            = findSequences(s, e, words);

        System.out.print("[");

        for (int i = 0; i < ans.size(); i++) {

            System.out.print("[");

            for (int j = 0; j < ans.get(i).size(); j++) {

                System.out.print("\"" + ans.get(i).get(j)
                                 + "\"");

                if (j + 1 < ans.get(i).size()) {
                    System.out.print(", ");
                }
            }

            System.out.print("]");

            if (i + 1 < ans.size()) {
                System.out.print(", ");
            }
        }

        System.out.print("]");
    }
}
Python
from collections import deque


def findSequences(words, s, e):

    st = set(words)

    # Queue stores complete transformation sequences.
    q = deque()
    q.append([s])

    # Stores words used at the current BFS level.
    usedOnLevel = []
    usedOnLevel.append(s)

    level = 0
    ans = []

    while q:

        path = q.popleft()

        # Remove words used in the previous level.
        if len(path) > level:

            level = len(path)

            for word in usedOnLevel:
                st.discard(word)

            usedOnLevel.clear()

        word = path[-1]

        # If target word is reached, store the sequence.
        if word == e:

            if not ans:
                ans.append(path[:])

            elif len(ans[0]) == len(path):
                ans.append(path[:])

        # Generate all possible one-character transformations.
        for i in range(len(word)):

            temp = list(word)

            for ch in range(ord('a'), ord('z') + 1):

                temp[i] = chr(ch)

                # Valid transformed word found.
                nextWord = "".join(temp)

                if nextWord in st:

                    path.append(nextWord)
                    q.append(path[:])

                    usedOnLevel.append(nextWord)

                    path.pop()

    return ans


if __name__ == "__main__":

    s = "der"
    e = "dfs"

    words = ["des", "der", "dfr", "dgt", "dfs"]

    ans = findSequences(words, s, e)

    print("[", end="")

    for i in range(len(ans)):

        print("[", end="")

        for j in range(len(ans[i])):

            print(f"\"{ans[i][j]}\"", end="")

            if j + 1 < len(ans[i]):
                print(", ", end="")

        print("]", end="")

        if i + 1 < len(ans):
            print(", ", end="")

    print("]")
C#
using System;
using System.Collections.Generic;

class GFG {
    static List<List<string> >
    findSequences(List<string> words, string s, string e)
    {
        HashSet<string> st = new HashSet<string>(words);

        // Queue stores complete transformation sequences.
        Queue<List<string> > q = new Queue<List<string> >();
        q.Enqueue(new List<string>{ s });

        // Stores words used at the current BFS level.
        List<string> usedOnLevel = new List<string>();
        usedOnLevel.Add(s);

        int level = 0;
        List<List<string> > ans = new List<List<string> >();

        while (q.Count > 0) {
            List<string> path = q.Dequeue();

            // Remove words used in the previous level.
            if (path.Count > level) {
                level = path.Count;

                foreach(string usedWord in usedOnLevel)
                    st.Remove(usedWord);

                usedOnLevel.Clear();
            }

            string word = path[path.Count - 1];

            // If target word is reached, store the
            // sequence.
            if (word == e) {
                if (ans.Count == 0)
                    ans.Add(new List<string>(path));

                else if (ans[0].Count == path.Count)
                    ans.Add(new List<string>(path));
            }

            // Generate all possible one-character
            // transformations.
            for (int i = 0; i < word.Length; i++) {
                char[] temp = word.ToCharArray();

                for (char ch = 'a'; ch <= 'z'; ch++) {
                    temp[i] = ch;
                    string nextWord = new string(temp);

                    // Valid transformed word found.
                    if (st.Contains(nextWord)) {
                        path.Add(nextWord);
                        q.Enqueue(new List<string>(path));

                        usedOnLevel.Add(nextWord);

                        path.RemoveAt(path.Count - 1);
                    }
                }
            }
        }

        return ans;
    }

    static void Main()
    {
        string s = "der";
        string e = "dfs";

        List<string> words
            = new List<string>{ "des", "der", "dfr", "dgt",
                                "dfs" };

        List<List<string> > ans
            = findSequences(words, s, e);

        Console.Write("[");

        for (int i = 0; i < ans.Count; i++) {
            Console.Write("[");

            for (int j = 0; j < ans[i].Count; j++) {
                Console.Write("\"" + ans[i][j] + "\"");

                if (j + 1 < ans[i].Count)
                    Console.Write(", ");
            }

            Console.Write("]");

            if (i + 1 < ans.Count)
                Console.Write(", ");
        }

        Console.Write("]");
    }
}
JavaScript
function findSequences(words, s, e)
{

    let st = new Set(words);

    // Queue stores complete transformation sequences.
    let q = [];
    q.push([ s ]);

    // Stores words used at the current BFS level.
    let usedOnLevel = [];
    usedOnLevel.push(s);

    let level = 0;
    let ans = [];

    while (q.length > 0) {

        let path = q.shift();

        // Remove words used in the previous level.
        if (path.length > level) {

            level = path.length;

            for (let word of usedOnLevel)
                st.delete(word);

            usedOnLevel = [];
        }

        let word = path[path.length - 1];

        // If target word is reached, store the sequence.
        if (word === e) {

            if (ans.length === 0)
                ans.push([...path ]);

            else if (ans[0].length === path.length)
                ans.push([...path ]);
        }

        // Generate all possible one-character
        // transformations.
        for (let i = 0; i < word.length; i++) {

            let temp = word.split("");

            for (let ch = 97; ch <= 122; ch++) {

                temp[i] = String.fromCharCode(ch);
                let nextWord = temp.join("");

                // Valid transformed word found.
                if (st.has(nextWord)) {

                    path.push(nextWord);
                    q.push([...path ]);

                    usedOnLevel.push(nextWord);

                    path.pop();
                }
            }
        }
    }

    return ans;
}

// driver code

let s = "der";
let e = "dfs";

let words = [ "des", "der", "dfr", "dgt", "dfs" ];

let ans = findSequences(words, s, e);

process.stdout.write("[");

for (let i = 0; i < ans.length; i++) {

    process.stdout.write("[");

    for (let j = 0; j < ans[i].length; j++) {

        process.stdout.write(`"${ans[i][j]}"`);

        if (j + 1 < ans[i].length)
            process.stdout.write(", ");
    }

    process.stdout.write("]");

    if (i + 1 < ans.length)
        process.stdout.write(", ");
}

process.stdout.write("]");

Output
[["der", "dfr", "dfs"], ["der", "des", "dfs"]]

[Expected Approach] BFS with Parent Tracking and DFS Backtracking - O(m * (n + k)) Time and O(n * m) Space

The idea is to use BFS because it always explores words level by level, guaranteeing that the first time we reach the target word, we have found the shortest transformation distance. Instead of storing complete paths during BFS, we build a parent graph, where each word stores all of its predecessors that can reach it through a shortest path. Since multiple shortest paths may reach the same word in the same BFS level, we keep all such parents. After BFS finishes, we perform DFS/backtracking from the target word to the source word using the parent graph to reconstruct every shortest transformation sequence.

Working of Approach:

  • Insert all words into a hash set for O(1) lookup and initialize BFS from the source word.
  • Perform level-order BFS and generate all possible one-character transformations by replacing each character from 'a' to 'z'.
  • For every valid transformed word, if it has not been visited in any previous level, add it to the queue only once in the current level using currLevelVisited, and store the current word as its parent in the parent map to track all shortest paths.
  • After processing the entire level, move all words in currLevelVisited to the visited set so they are not revisited in deeper levels while still allowing multiple shortest paths within the same level.
  • Complete the current BFS level after endWord is first found, then stop the BFS, because BFS guarantees that all paths reaching the target in that level are the shortest.
  • Start DFS/backtracking from the target word using the parent map to reconstruct all shortest transformation sequences.
C++
#include <vector>
#include <unordered_map>
#include <unordered_set>
#include <queue>
#include <string>
#include <algorithm>
#include <iostream>
using namespace std;

// DFS Backtracking
// Build all sequences from end word back to start word
void buildPaths(string word, string &beginWord, vector<string> &path,
                unordered_map<string, vector<string>> &parent, vector<vector<string>> &ans)
{
    // Base Case:
    // Reached starting word
    if (word == beginWord)
    {
        vector<string> temp = path;

        // Currently path is in reverse order
        reverse(temp.begin(), temp.end());

        ans.push_back(temp);
        return;
    }

    // Traverse all parents of current word
    for (string par : parent[word])
    {
        path.push_back(par);

        buildPaths(par, beginWord, path, parent, ans);

        // Backtrack
        path.pop_back();
    }
}

vector<vector<string>> findSequences(vector<string> &words, string &s, string &e)
{

    // Parent map:
    // child -> all possible parents
    unordered_map<string, vector<string>> parent;

    // Final answer
    vector<vector<string>> ans;

    unordered_set<string> st(words.begin(), words.end());

    // If target word does not exist
    if (st.find(e) == st.end())
    {
        return {};
    }

    queue<string> q;

    q.push(s);

    // Visited words of current level
    // (important for multiple shortest paths)
    unordered_set<string> visited;

    // Mark begin word visited
    visited.insert(s);

    bool found = false;

    while (!q.empty() &&!found)
    {
        int size = q.size();

        // Words visited in current level only
        unordered_set<string> currLevelVisited;

        while (size--)
        {
            string word = q.front();
            q.pop();

            string original = word;

            for (int i = 0; i < word.size(); i++)
            {
                char org = word[i];

                // Replace with 'a' to 'z'
                for (char ch = 'a'; ch <= 'z'; ch++)
                {
                    word[i] = ch;

                    if (st.find(word)!= st.end())
                    {
                        // If not visited before
                        if (visited.find(word) == visited.end())
                        {
                            // First time in this level
                            if (currLevelVisited.find(word) == currLevelVisited.end())
                            {
                                q.push(word);

                                currLevelVisited.insert(word);
                            }

                            parent[word].push_back(original);

                            // If target found
                            if (word == e)
                            {
                                found = true;
                            }
                        }
                    }
                }

                // Restore original character
                word[i] = org;
            }
        }

        // Mark current level words as globally visited
        for (auto word : currLevelVisited)
        {
            visited.insert(word);
        }
    }

    // If no sequence exists
    if (!found)
    {
        return {};
    }

    // Backtracking DFS
    vector<string> path;

    // Start from end word
    path.push_back(e);

    buildPaths(e, s, path, parent, ans);

    return ans;
}

int main()
{
    string s = "der";
    string e = "dfs";

    vector<string> words = {"des", "der", "dfr", "dgt", "dfs"};

    vector<vector<string>> ans = findSequences(words, s, e);

    cout << "[";

    for (int i = 0; i < ans.size(); i++)
    {
        cout << "[";

        for (int j = 0; j < ans[i].size(); j++)
        {
            cout << '"' << ans[i][j] << '"';

            if (j + 1 < ans[i].size())
                cout << ", ";
        }

        cout << "]";

        if (i + 1 < ans.size())
            cout << ", ";
    }

    cout << "]";

    return 0;
}
Java
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Queue;

// DFS Backtracking
// Build all sequences from end word back to start word
public class GFG {

    static void
    buildPaths(String word, String beginWord,
              ArrayList<String> path,
              HashMap<String, ArrayList<String> > parent,
              ArrayList<ArrayList<String> > ans)
    {

        // Base Case:
        // Reached starting word
        if (word.equals(beginWord)) {
            ArrayList<String> temp = new ArrayList<>(path);

            // Currently path is in reverse order
            java.util.Collections.reverse(temp);

            ans.add(temp);
            return;
        }

        // Traverse all parents of current word
        if (!parent.containsKey(word))
            return;

        for (String par : parent.get(word)) {
            path.add(par);

            buildPaths(par, beginWord, path, parent, ans);

            // Backtrack
            path.remove(path.size() - 1);
        }
    }

    static ArrayList<ArrayList<String> >
    findSequences(String[] words, String s, String e)
    {

        // Parent map:
        // child -> all possible parents
        HashMap<String, ArrayList<String> > parent
            = new HashMap<>();

        // Final answer
        ArrayList<ArrayList<String> > ans
            = new ArrayList<>();

        HashSet<String> st
            = new HashSet<>(java.util.Arrays.asList(words));

        // If target word does not exist
        if (!st.contains(e)) {
            return new ArrayList<>();
        }

        Queue<String> q = new LinkedList<>();

        q.offer(s);

        // Visited words of current level
        // (important for multiple shortest paths)
        HashSet<String> visited = new HashSet<>();

        // Mark begin word visited
        visited.add(s);

        boolean found = false;

        while (!q.isEmpty() &&!found) {
            int size = q.size();

            // Words visited in current level only
            HashSet<String> currLevelVisited
                = new HashSet<>();

            while (size-- > 0) {
                String word = q.poll();

                String original = word;

                char[] chars = word.toCharArray();

                for (int i = 0; i < chars.length; i++) {
                    char org = chars[i];

                    // Replace with 'a' to 'z'
                    for (char ch = 'a'; ch <= 'z'; ch++) {
                        chars[i] = ch;
                        word = new String(chars);

                        if (st.contains(word)) {

                            // If not visited before
                            if (!visited.contains(word)) {

                                // First time in this level
                                if (!currLevelVisited
                                         .contains(word)) {
                                    q.offer(word);

                                    currLevelVisited.add(
                                        word);
                                }

                                parent.putIfAbsent(
                                    word,
                                    new ArrayList<>());
                                parent.get(word).add(
                                    original);

                                // If target found
                                if (word.equals(e)) {
                                    found = true;
                                }
                            }
                        }
                    }

                    // Restore original character
                    chars[i] = org;
                }
            }

            // Mark current level words as globally visited
            for (String word : currLevelVisited) {
                visited.add(word);
            }
        }

        // If no sequence exists
        if (!found) {
            return new ArrayList<>();
        }

        // Backtracking DFS
        ArrayList<String> path = new ArrayList<>();

        // Start from end word
        path.add(e);

        buildPaths(e, s, path, parent, ans);

        return ans;
    }

    public static void main(String[] args)
    {

        String s = "der";
        String e = "dfs";

        String[] words
            = { "des", "der", "dfr", "dgt", "dfs" };

        ArrayList<ArrayList<String> > ans
            = findSequences(words, s, e);

        System.out.print("[");

        for (int i = 0; i < ans.size(); i++) {
            System.out.print("[");

            for (int j = 0; j < ans.get(i).size(); j++) {
                System.out.print("\"" + ans.get(i).get(j)
                                 + "\"");

                if (j + 1 < ans.get(i).size())
                    System.out.print(", ");
            }

            System.out.print("]");

            if (i + 1 < ans.size())
                System.out.print(", ");
        }

        System.out.println("]");
    }
}
Python
# DFS Backtracking
# Build all sequences from end word back to start word
def buildPaths(word, beginWord, path, parent, ans):
    # Base Case:
    # Reached starting word
    if word == beginWord:
        temp = path.copy()

        # Currently path is in reverse order
        temp.reverse()

        ans.append(temp)
        return

    # Traverse all parents of current word
    for par in parent[word]:
        path.append(par)

        buildPaths(par, beginWord, path, parent, ans)

        # Backtrack
        path.pop()


def findSequences(words, s, e):
    # Parent map:
    # child -> all possible parents
    parent = {}

    # Final answer
    ans = []

    st = set(words)

    # If target word does not exist
    if e not in st:
        return []

    q = [s]

    # Visited words of current level
    # (important for multiple shortest paths)
    visited = set()

    # Mark begin word visited
    visited.add(s)

    found = False

    while q and not found:
        size = len(q)

        # Words visited in current level only
        currLevelVisited = set()

        while size > 0:
            size -= 1
            word = q.pop(0)
            original = word

            for i in range(len(word)):
                org = word[i]

                # Replace with 'a' to 'z'
                for ch in range(ord('a'), ord('z') + 1):
                    word = word[:i] + chr(ch) + word[i + 1:]

                    if word in st:
                        # If not visited before
                        if word not in visited:
                            # First time in this level
                            if word not in currLevelVisited:
                                q.append(word)
                                currLevelVisited.add(word)

                            if word not in parent:
                                parent[word] = []
                            parent[word].append(original)

                            # If target found
                            if word == e:
                                found = True

                    # Restore original character
                    word = word[:i] + org + word[i + 1:]

        # Mark current level words as globally visited
        visited.update(currLevelVisited)

    # If no sequence exists
    if not found:
        return []

    # Backtracking DFS
    path = []

    # Start from end word
    path.append(e)

    buildPaths(e, s, path, parent, ans)

    return ans


if __name__ == '__main__':
    s = "der"
    e = "dfs"

    words = ["des", "der", "dfr", "dgt", "dfs"]

    ans = findSequences(words, s, e)

    print('[')

    for i in range(len(ans)):
        print('[', end='')

        for j in range(len(ans[i])):
            print('"' + ans[i][j] + '"', end='')

            if j + 1 < len(ans[i]):
                print(', ', end='')

        print(']', end='')

        if i + 1 < len(ans):
            print(', ', end='')

    print(']')
C#
using System;
using System.Collections.Generic;

// DFS Backtracking
// Build all sequences from end word back to start word
class GFG {
    public static void
    buildPaths(string word, string beginWord,
               List<string> path,
               Dictionary<string, List<string> > parent,
               List<List<string> > ans)
    {
        // Base Case:
        // Reached starting word
        if (word == beginWord) {
            List<string> temp = new List<string>(path);

            // Currently path is in reverse order
            temp.Reverse();

            ans.Add(temp);
            return;
        }

        // Traverse all parents of current word
        if (!parent.ContainsKey(word))
            return;

        foreach(string par in parent[word])
        {
            path.Add(par);

            buildPaths(par, beginWord, path, parent, ans);

            // Backtrack
            path.RemoveAt(path.Count - 1);
        }
    }

    public static List<List<string> >
    findSequences(string[] words, string s, string e)
    {
        // Parent map:
        // child -> all possible parents
        Dictionary<string, List<string> > parent
            = new Dictionary<string, List<string> >();

        // Final answer
        List<List<string> > ans = new List<List<string> >();

        HashSet<string> st = new HashSet<string>(words);

        // If target word does not exist
        if (!st.Contains(e)) {
            return new List<List<string> >();
        }

        Queue<string> q = new Queue<string>();

        q.Enqueue(s);

        // Visited words of current level
        // (important for multiple shortest paths)
        HashSet<string> visited = new HashSet<string>();

        // Mark begin word visited
        visited.Add(s);

        bool found = false;

        while (q.Count > 0 && !found) {
            int size = q.Count;

            // Words visited in current level only
            HashSet<string> currLevelVisited
                = new HashSet<string>();

            while (size-- > 0) {
                string word = q.Dequeue();

                string original = word;

                char[] chars = word.ToCharArray();

                for (int i = 0; i < chars.Length; i++) {
                    char org = chars[i];

                    // Replace with 'a' to 'z'
                    for (char ch = 'a'; ch <= 'z'; ch++) {
                        chars[i] = ch;
                        word = new string(chars);

                        if (st.Contains(word)) {
                            // If not visited before
                            if (!visited.Contains(word)) {
                                // First time in this level
                                if (!currLevelVisited
                                         .Contains(word)) {
                                    q.Enqueue(word);

                                    currLevelVisited.Add(
                                        word);
                                }

                                if (!parent.ContainsKey(
                                        word)) {
                                    parent[word] = new List<
                                        string>();
                                }

                                parent[word].Add(original);

                                // If target found
                                if (word == e) {
                                    found = true;
                                }
                            }
                        }
                    }

                    // Restore original character
                    chars[i] = org;
                }
            }

            // Mark current level words as globally visited
            foreach(string word in currLevelVisited)
            {
                visited.Add(word);
            }
        }

        // If no sequence exists
        if (!found) {
            return new List<List<string> >();
        }

        // Backtracking DFS
        List<string> path = new List<string>();

        // Start from end word
        path.Add(e);

        buildPaths(e, s, path, parent, ans);

        return ans;
    }

    public static void Main(string[] args)
    {
        string s = "der";
        string e = "dfs";

        string[] words
            = { "des", "der", "dfr", "dgt", "dfs" };

        List<List<string> > ans
            = findSequences(words, s, e);

        Console.Write("[");

        for (int i = 0; i < ans.Count; i++) {
            Console.Write("[");

            for (int j = 0; j < ans[i].Count; j++) {
                Console.Write("\"" + ans[i][j] + "\"");

                if (j + 1 < ans[i].Count)
                    Console.Write(", ");
            }

            Console.Write("]");

            if (i + 1 < ans.Count)
                Console.Write(", ");
        }

        Console.WriteLine("]");
    }
}
JavaScript
// DFS Backtracking
// Build all sequences from end word back to start word
function buildPaths(word, beginWord, path, parent, ans)
{
    // Base Case:
    // Reached starting word
    if (word === beginWord) {
        let temp = [...path ];

        // Currently path is in reverse order
        temp.reverse();

        ans.push(temp);
        return;
    }

    // Traverse all parents of current word
    if (!parent.has(word))
        return;

    for (let par of parent.get(word)) {
        path.push(par);

        buildPaths(par, beginWord, path, parent, ans);

        // Backtrack
        path.pop();
    }
}

function findSequences(words, s, e)
{
    // Parent map:
    // child -> all possible parents
    let parent = new Map();

    // Final answer
    let ans = [];

    let st = new Set(words);

    // If target word does not exist
    if (!st.has(e)) {
        return [];
    }

    let q = [];

    q.push(s);

    // Visited words of current level
    // (important for multiple shortest paths)
    let visited = new Set();

    // Mark begin word visited
    visited.add(s);

    let found = false;

    while (q.length > 0 && !found) {
        let size = q.length;

        // Words visited in current level only
        let currLevelVisited = new Set();

        while (size--) {
            let word = q.shift();

            let original = word;

            let chars = word.split("");

            for (let i = 0; i < chars.length; i++) {
                let org = chars[i];

                // Replace with 'a' to 'z'
                for (let ch = 97; ch <= 122; ch++) {
                    chars[i] = String.fromCharCode(ch);
                    word = chars.join("");

                    if (st.has(word)) {
                        // If not visited before
                        if (!visited.has(word)) {
                            // First time in this level
                            if (!currLevelVisited.has(
                                    word)) {
                                q.push(word);

                                currLevelVisited.add(word);
                            }

                            if (!parent.has(word)) {
                                parent.set(word, []);
                            }

                            parent.get(word).push(original);

                            // If target found
                            if (word === e) {
                                found = true;
                            }
                        }
                    }
                }

                // Restore original character
                chars[i] = org;
            }
        }

        // Mark current level words as globally visited
        for (let word of currLevelVisited) {
            visited.add(word);
        }
    }

    // If no sequence exists
    if (!found) {
        return [];
    }

    // Backtracking DFS
    let path = [];

    // Start from end word
    path.push(e);

    buildPaths(e, s, path, parent, ans);

    return ans;
}

// driver code
let s = "der";
let e = "dfs";

let words = [ "des", "der", "dfr", "dgt", "dfs" ];

let ans = findSequences(words, s, e);

process.stdout.write("[");

for (let i = 0; i < ans.length; i++) {
    process.stdout.write("[");

    for (let j = 0; j < ans[i].length; j++) {
        process.stdout.write("\"" + ans[i][j] + "\"");

        if (j + 1 < ans[i].length)
            process.stdout.write(", ");
    }

    process.stdout.write("]");

    if (i + 1 < ans.length)
        process.stdout.write(", ");
}

console.log("]");

Output
[["der", "dfr", "dfs"], ["der", "des", "dfs"]]

[Alternate Approach] Bidirectional BFS with Backtracking - O(m * (n + k)) Time and O(n * m) Space

The idea is to perform Bidirectional BFS from both the source and target words simultaneously. By always expanding the smaller frontier, the number of explored states is reduced significantly. Once the two searches meet, DFS backtracking is used to reconstruct all shortest transformation sequences.

Let us understand with example:
Input: s = "der", e = "dfs", words[] = ["des", "der", "dfr", "dgt", "dfs"]

  • Start Bidirectional BFS with beginSet = {der} and endSet = {dfs}.
  • From "der", we generate "des" and "dfr", so edges der -> des and der -> dfr are added.
  • Since the forward frontier becomes larger, the frontiers are swapped and BFS continues from "dfs".
  • While processing "dfs", the words "des" and "dfr" are found in the opposite frontier, so edges des -> dfs and dfr -> dfs are added.
  • DFS on the graph generates all shortest sequences: der -> des -> dfs and der -> dfr -> dfs.
C++
#include <string>
#include <vector>
#include <unordered_map>
#include <unordered_set>
#include <iostream>

using namespace std;

// Uses DFS to reconstruct all shortest sequences.
void dfs(string word, string &e, unordered_map<string, vector<string>> &adj, vector<string> &path, vector<vector<string>> &ans)
{
    // Reached the target word.
    if (word == e)
    {
        ans.push_back(path);
        return;
    }

    // Explore all possible next words.
    for (string &next : adj[word])
    {
        path.push_back(next);

        dfs(next, e, adj, path, ans);

        path.pop_back();
    }
}

vector<vector<string>> findSequences(vector<string> &words, string &s, string &e)
{
    unordered_set<string> dict(words.begin(), words.end());

    vector<vector<string>> ans;

    // Target word must be present in dictionary.
    if (!dict.count(e))
        return ans;

    unordered_set<string> beginSet;
    unordered_set<string> endSet;

    beginSet.insert(s);
    endSet.insert(e);

    // Stores shortest-path relationships.
    unordered_map<string, vector<string>> adj;

    bool found = false;
    bool reverseEdge = false;

    dict.erase(s);
    dict.erase(e);

    // Perform Bidirectional BFS.
    while (!beginSet.empty() &&!endSet.empty() &&!found)
    {
        // Always expand the smaller frontier.
        if (beginSet.size() > endSet.size())
        {
            swap(beginSet, endSet);
            reverseEdge =!reverseEdge;
        }

        unordered_set<string> nextLevel;

        // Remove current frontier words.
        for (string word : beginSet)
            dict.erase(word);

        for (string word : endSet)
            dict.erase(word);

        for (string word : beginSet)
        {
            string temp = word;

            // Generate all one-character transformations.
            for (int i = 0; i < temp.size(); i++)
            {
                char original = temp[i];

                for (char ch = 'a'; ch <= 'z'; ch++)
                {
                    if (ch == original)
                        continue;

                    temp[i] = ch;

                    string parent = reverseEdge? temp : word;

                    string child = reverseEdge? word : temp;

                    // Frontiers meet.
                    if (endSet.count(temp))
                    {
                        found = true;
                        adj[parent].push_back(child);
                    }

                    // Valid intermediate word.
                    else if (!found && dict.count(temp))
                    {
                        nextLevel.insert(temp);
                        adj[parent].push_back(child);
                    }
                }

                temp[i] = original;
            }
        }

        beginSet = nextLevel;
    }

    if (!found)
        return ans;

    // Reconstruct all shortest paths.
    vector<string> path;
    path.push_back(s);

    dfs(s, e, adj, path, ans);

    return ans;
}

int main()
{
    string s = "der";
    string e = "dfs";

    vector<string> words = {"des", "der", "dfr", "dgt", "dfs"};

    vector<vector<string>> ans = findSequences(words, s, e);

    cout << "[";

    for (int i = 0; i < ans.size(); i++)
    {
        cout << "[";

        for (int j = 0; j < ans[i].size(); j++)
        {
            cout << '"' << ans[i][j] << '"';

            if (j + 1 < ans[i].size())
                cout << ", ";
        }

        cout << "]";

        if (i + 1 < ans.size())
            cout << ", ";
    }

    cout << "]";

    return 0;
}
Java
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;

public class GFG {

    // Uses DFS to reconstruct all shortest sequences.
    static void dfs(String word, String e,
                    HashMap<String, ArrayList<String> > adj,
                    ArrayList<String> path,
                    ArrayList<ArrayList<String> > ans)
    {
        // Reached the target word.
        if (word.equals(e)) {
            ans.add(new ArrayList<>(path));
            return;
        }

        // Explore all possible next words.
        if (!adj.containsKey(word))
            return;

        for (String next : adj.get(word)) {
            path.add(next);

            dfs(next, e, adj, path, ans);

            path.remove(path.size() - 1);
        }
    }

    static ArrayList<ArrayList<String> > findSequences(String[] words, String s, String e)
    {
        HashSet<String> dict = new HashSet<>(Arrays.asList(words));

        ArrayList<ArrayList<String> > ans = new ArrayList<>();

        // Target word must be present in dictionary.
        if (!dict.contains(e))
            return ans;

        HashSet<String> beginSet = new HashSet<>();
        HashSet<String> endSet = new HashSet<>();

        beginSet.add(s);
        endSet.add(e);

        // Stores shortest-path relationships.
        HashMap<String, ArrayList<String> > adj = new HashMap<>();

        boolean found = false;
        boolean reverseEdge = false;

        dict.remove(s);
        dict.remove(e);

        // Perform Bidirectional BFS.
        while (!beginSet.isEmpty() &&!endSet.isEmpty() && !found) {

            // Always expand the smaller frontier.
            if (beginSet.size() > endSet.size()) {
                HashSet<String> tempSet = beginSet;
                beginSet = endSet;
                endSet = tempSet;

                reverseEdge = !reverseEdge;
            }

            HashSet<String> nextLevel = new HashSet<>();

            // Remove current frontier words.
            for (String word : beginSet)
                dict.remove(word);

            for (String word : endSet)
                dict.remove(word);

            for (String word : beginSet) {
                char[] temp = word.toCharArray();

                // Generate all one-character transformations.
                for (int i = 0; i < temp.length; i++) {
                    char original = temp[i];

                    for (char ch = 'a'; ch <= 'z'; ch++) {
                        if (ch == original)
                            continue;

                        temp[i] = ch;

                        String transformed = new String(temp);

                        String parent = reverseEdge ? transformed : word;
                        String child = reverseEdge ? word : transformed;

                        // Frontiers meet.
                        if (endSet.contains(transformed)) {
                            found = true;

                            adj.computeIfAbsent(parent, k -> new ArrayList<>())
                                    .add(child);
                        }

                        // Valid intermediate word.
                        else if (!found && dict.contains(transformed)) {
                            nextLevel.add(transformed);

                            adj.computeIfAbsent(parent, k -> new ArrayList<>())
                                    .add(child);
                        }
                    }

                    temp[i] = original;
                }
            }

            beginSet = nextLevel;
        }

        if (!found)
            return ans;

        // Reconstruct all shortest paths.
        ArrayList<String> path = new ArrayList<>();
        path.add(s);

        dfs(s, e, adj, path, ans);

        return ans;
    }

    public static void main(String[] args)
    {
        String s = "der";
        String e = "dfs";

        String[] words = { "des", "der", "dfr", "dgt", "dfs" };

        ArrayList<ArrayList<String> > ans = findSequences(words, s, e);

        System.out.print("[");

        for (int i = 0; i < ans.size(); i++) {
            System.out.print("[");

            for (int j = 0; j < ans.get(i).size(); j++) {

                System.out.print("\"" + ans.get(i).get(j) + "\"");

                if (j + 1 < ans.get(i).size())
                    System.out.print(", ");
            }

            System.out.print("]");

            if (i + 1 < ans.size())
                System.out.print(", ");
        }

        System.out.print("]");
    }
}
Python
from collections import deque

# Uses DFS to reconstruct all shortest sequences.
def dfs(word, e, adj, path, ans):

    # Reached the target word.
    if word == e:
        ans.append(path[:])
        return

    # Explore all possible next words.
    for nxt in adj.get(word, []):
        path.append(nxt)

        dfs(nxt, e, adj, path, ans)

        path.pop()


def findSequences(words, s, e):
    dict_set = set(words)

    ans = []

    # Target word must be present in dictionary.
    if e not in dict_set:
        return ans

    beginSet = {s}
    endSet = {e}

    # Stores shortest-path relationships.
    adj = {}

    found = False
    reverseEdge = False

    dict_set.discard(s)
    dict_set.discard(e)

    # Perform Bidirectional BFS.
    while beginSet and endSet and not found:

        # Always expand the smaller frontier.
        if len(beginSet) > len(endSet):
            beginSet, endSet = endSet, beginSet
            reverseEdge = not reverseEdge

        nextLevel = set()

        # Remove current frontier words.
        for word in beginSet:
            dict_set.discard(word)

        for word in endSet:
            dict_set.discard(word)

        for word in beginSet:
            temp = list(word)

            # Generate all one-character transformations.
            for i in range(len(temp)):
                original = temp[i]

                for ch in range(ord('a'), ord('z') + 1):
                    ch = chr(ch)

                    if ch == original:
                        continue

                    temp[i] = ch
                    transformed = ''.join(temp)

                    parent = transformed if reverseEdge else word
                    child = word if reverseEdge else transformed

                    # Frontiers meet.
                    if transformed in endSet:
                        found = True

                        if parent not in adj:
                            adj[parent] = []

                        adj[parent].append(child)

                    # Valid intermediate word.
                    elif not found and transformed in dict_set:
                        nextLevel.add(transformed)

                        if parent not in adj:
                            adj[parent] = []

                        adj[parent].append(child)

                temp[i] = original

        beginSet = nextLevel

    if not found:
        return ans

    # Reconstruct all shortest paths.
    path = [s]

    dfs(s, e, adj, path, ans)

    return ans


if __name__ == "__main__":
    s = "der"
    e = "dfs"

    words = ["des", "der", "dfr", "dgt", "dfs"]

    ans = findSequences(words, s, e)

    print("[", end="")

    for i in range(len(ans)):
        print("[", end="")

        for j in range(len(ans[i])):
            print(f"\"{ans[i][j]}\"", end="")

            if j + 1 < len(ans[i]):
                print(", ", end="")

        print("]", end="")

        if i + 1 < len(ans):
            print(", ", end="")

    print("]")
C#
using System;
using System.Collections.Generic;

class GFG {
    // Uses DFS to reconstruct all shortest sequences.
    private void DFS(string word, string e,
                     Dictionary<string, List<string> > adj,
                     List<string> path,
                     List<List<string> > ans)
    {
        // Reached the target word.
        if (word == e) {
            ans.Add(new List<string>(path));
            return;
        }

        // Explore all possible next words.
        if (!adj.ContainsKey(word))
            return;

        foreach(string next in adj[word])
        {
            path.Add(next);

            DFS(next, e, adj, path, ans);

            path.RemoveAt(path.Count - 1);
        }
    }

    public List<List<string> >
    findSequences(string[] words, string s, string e)
    {
        HashSet<string> dict = new HashSet<string>(words);

        List<List<string> > ans = new List<List<string> >();

        // Target word must be present in dictionary.
        if (!dict.Contains(e))
            return ans;

        HashSet<string> beginSet = new HashSet<string>();
        HashSet<string> endSet = new HashSet<string>();

        beginSet.Add(s);
        endSet.Add(e);

        // Stores shortest-path relationships.
        Dictionary<string, List<string> > adj
            = new Dictionary<string, List<string> >();

        bool found = false;
        bool reverseEdge = false;

        dict.Remove(s);
        dict.Remove(e);

        // Perform Bidirectional BFS.
        while (beginSet.Count > 0 && endSet.Count > 0
               && !found) {
            // Always expand the smaller frontier.
            if (beginSet.Count > endSet.Count) {
                HashSet<string> tempSet = beginSet;
                beginSet = endSet;
                endSet = tempSet;

                reverseEdge = !reverseEdge;
            }

            HashSet<string> nextLevel
                = new HashSet<string>();

            // Remove current frontier words.
            foreach(string word in beginSet)
                dict.Remove(word);

            foreach(string word in endSet)
                dict.Remove(word);

            foreach(string word in beginSet)
            {
                char[] temp = word.ToCharArray();

                // Generate all one-character
                // transformations.
                for (int i = 0; i < temp.Length; i++) {
                    char original = temp[i];

                    for (char ch = 'a'; ch <= 'z'; ch++) {
                        if (ch == original)
                            continue;

                        temp[i] = ch;

                        string transformed
                            = new string(temp);

                        string parent = reverseEdge
                                            ? transformed
                                            : word;

                        string child = reverseEdge
                                           ? word
                                           : transformed;

                        // Frontiers meet.
                        if (endSet.Contains(transformed)) {
                            found = true;

                            if (!adj.ContainsKey(parent))
                                adj[parent]
                                    = new List<string>();

                            adj[parent].Add(child);
                        }

                        // Valid intermediate word.
                        else if (!found
                                 && dict.Contains(
                                     transformed)) {
                            nextLevel.Add(transformed);

                            if (!adj.ContainsKey(parent))
                                adj[parent]
                                    = new List<string>();

                            adj[parent].Add(child);
                        }
                    }

                    temp[i] = original;
                }
            }

            beginSet = nextLevel;
        }

        if (!found)
            return ans;

        // Reconstruct all shortest paths.
        List<string> path = new List<string>();
        path.Add(s);

        DFS(s, e, adj, path, ans);

        return ans;
    }

    static void Main()
    {
        string s = "der";
        string e = "dfs";

        string[] words
            = { "des", "der", "dfr", "dgt", "dfs" };

        GFG obj = new GFG();

        List<List<string> > ans
            = obj.findSequences(words, s, e);

        Console.Write("[");

        for (int i = 0; i < ans.Count; i++) {
            Console.Write("[");

            for (int j = 0; j < ans[i].Count; j++) {
                Console.Write("\"" + ans[i][j] + "\"");

                if (j + 1 < ans[i].Count)
                    Console.Write(", ");
            }

            Console.Write("]");

            if (i + 1 < ans.Count)
                Console.Write(", ");
        }

        Console.Write("]");
    }
}
JavaScript
// Uses DFS to reconstruct all shortest sequences.
function dfs(word, e, adj, path, ans)
{
    // Reached the target word.
    if (word === e) {
        ans.push([...path ]);
        return;
    }

    // Explore all possible next words.
    for (const next of (adj.get(word) || [])) {
        path.push(next);

        dfs(next, e, adj, path, ans);

        path.pop();
    }
}

function findSequences(words, s, e)
{
    const dict = new Set(words);

    const ans = [];

    // Target word must be present in dictionary.
    if (!dict.has(e))
        return ans;

    let beginSet = new Set([ s ]);
    let endSet = new Set([ e ]);

    // Stores shortest-path relationships.
    const adj = new Map();

    let found = false;
    let reverseEdge = false;

    dict.delete(s);
    dict.delete(e);

    // Perform Bidirectional BFS.
    while (beginSet.size && endSet.size && !found) {
        // Always expand the smaller frontier.
        if (beginSet.size > endSet.size) {
            [beginSet, endSet] = [ endSet, beginSet ];

            reverseEdge = !reverseEdge;
        }

        const nextLevel = new Set();

        // Remove current frontier words.
        for (const word of beginSet)
            dict.delete(word);

        for (const word of endSet)
            dict.delete(word);

        for (const word of beginSet) {
            // Generate all one-character transformations.
            for (let i = 0; i < word.length; i++) {
                const arr = word.split("");

                const original = arr[i];

                for (let c = 97; c <= 122; c++) {
                    const ch = String.fromCharCode(c);

                    if (ch === original)
                        continue;

                    arr[i] = ch;

                    const transformed = arr.join("");

                    const parent
                        = reverseEdge ? transformed : word;

                    const child
                        = reverseEdge ? word : transformed;

                    // Frontiers meet.
                    if (endSet.has(transformed)) {
                        found = true;

                        if (!adj.has(parent))
                            adj.set(parent, []);

                        adj.get(parent).push(child);
                    }

                    // Valid intermediate word.
                    else if (!found
                             && dict.has(transformed)) {
                        nextLevel.add(transformed);

                        if (!adj.has(parent))
                            adj.set(parent, []);

                        adj.get(parent).push(child);
                    }
                }
            }
        }

        beginSet = nextLevel;
    }

    if (!found)
        return ans;

    // Reconstruct all shortest paths.
    const path = [ s ];

    dfs(s, e, adj, path, ans);

    return ans;
}

// driver code
let s = "der";
let e = "dfs";

let words = [ "des", "der", "dfr", "dgt", "dfs" ];

let ans = findSequences(words, s, e);

console.log(JSON.stringify(ans));

Output
[["der", "dfr", "dfs"], ["der", "des", "dfs"]]
Comment