Open In App

Word Ladder – Set 2 ( Bi-directional BFS )

Given a dictionary, and two words start and target (both of the same length). Find length of the smallest chain from start to target if it exists, such that adjacent words in the chain only differ by one character and each word in the chain is a valid word i.e., it exists in the dictionary. It may be assumed that the target word exists in the dictionary and the lengths of all the dictionary words are equal.
Examples: 
 

Input: Dictionary = {POON, PLEE, SAME, POIE, PLEA, PLIE, POIN} 
start = “TOON” 
target = “PLEA” 
Output:
TOON -> POON –> POIN –> POIE –> PLIE –> PLEE –> PLEA 
 

 

Approach: This problem can be solved using the standard BFS approach as discussed here but its performance can be improved by using bi-directional BFS. 
Bi-directional BFS doesn’t reduce the time complexity of the solution but it definitely optimizes the performance in many cases. This approach can also be used in many other shortest pathfinding problems where we have sufficient information about the source and the target node. The basic idea involved in bi-directional BFS is to start the search from both the ends of the path. 
Therefore, two queues and two visited arrays are needed to be maintained to track both the paths. So, whenever a node (say A) is present in the source queue, encounters a node (say B) which is present in the target queue, then we can calculate the answer by adding the distance of A from source and the distance of B from target minus 1 (one node is common). This way we can calculate the answer in half the time as compared to the standard BFS approach. This method is also known as the meet-in-the-middle BFS approach.
Below is the implementation of the above approach:
 




// C++ implementation of the approach
#include <bits/stdc++.h>
using namespace std;
 
// Structure for queue
struct node {
    string word;
    int len;
};
 
// Function that returns true if a and b
// differ in only a single character
bool isAdj(string a, string b)
{
    int count = 0;
    for (int i = 0; i < a.length(); i++) {
        if (a[i] != b[i])
            count++;
    }
    if (count == 1)
        return true;
    return false;
}
 
// Function to return the length of the shortest
// chain from ‘beginWord’ to ‘endWord’
int ladderLength(string beginWord, string endWord,
                 vector<string>& wordList)
{
 
    /* q1 is used to traverse the graph from beginWord
        and q2 is used to traverse the graph from endWord.
        vis1 and vis2 are used to keep track of the
        visited states from respective directions */
    queue<node> q1;
    queue<node> q2;
    unordered_map<string, int> vis1;
    unordered_map<string, int> vis2;
 
    node start = { beginWord, 1 };
    node end = { endWord, 1 };
 
    vis1[beginWord] = 1;
    q1.push(start);
    vis2[endWord] = 1;
    q2.push(end);
 
    while (!q1.empty() && !q2.empty()) {
 
        // Fetch the current node
        // from the source queue
        node curr1 = q1.front();
        q1.pop();
 
        // Fetch the current node from
        // the destination queue
        node curr2 = q2.front();
        q2.pop();
 
        // Check all the neighbors of curr1
        for (auto it = wordList.begin(); it != wordList.end(); it++) {
 
            // If any one of them is adjacent to curr1
            // and is not present in vis1
            // then push it in the queue
            if (isAdj(curr1.word, *it) && vis1.count(*it) == false) {
 
                node temp = { *it, curr1.len + 1 };
                q1.push(temp);
                vis1[*it] = curr1.len + 1;
 
                // If temp is the destination node
                // then return the answer
                if (temp.word == endWord) {
                    return temp.len;
                }
 
                // If temp is present in vis2 i.e. distance from
                // temp and the destination is already calculated
                if (vis2.count(temp.word)) {
                    return temp.len + vis2[temp.word] - 1;
                }
            }
        }
 
        // Check all the neighbors of curr2
        for (auto it = wordList.begin(); it != wordList.end(); it++) {
 
            // If any one of them is adjacent to curr2
            // and is not present in vis1 then push it in the queue.
            if (isAdj(curr2.word, *it) && vis2.count(*it) == false) {
 
                node temp = { *it, curr2.len + 1 };
                q2.push(temp);
                vis2[*it] = curr2.len + 1;
 
                // If temp is the destination node
                // then return the answer
                if (temp.word == beginWord) {
                    return temp.len;
                }
 
                // If temp is present in vis1 i.e. distance from
                // temp and the source is already calculated
                if (vis1.count(temp.word)) {
                    return temp.len + vis1[temp.word] - 1;
                }
            }
        }
    }
    return 0;
}
 
// Driver code
int main()
{
 
    vector<string> wordList;
    wordList.push_back("poon");
    wordList.push_back("plee");
    wordList.push_back("same");
    wordList.push_back("poie");
    wordList.push_back("plie");
    wordList.push_back("poin");
    wordList.push_back("plea");
 
    string start = "toon";
    string target = "plea";
 
    cout << ladderLength(start, target, wordList);
 
    return 0;
}




import java.util.*;
public class GFG
{
public static class node
{
    String word;
    int len;
    public node(String word, int len)
    {
        this.word = word;
        this.len = len;
    }
}
 
public static boolean isAdj(String a, String b)
{
    int count = 0;
    for (int i = 0; i < a.length(); i++)
    {
        if (a.charAt(i) != b.charAt(i))
            count++;
    }
    if (count == 1)
        return true;
    return false;
}
 
// Function to return the length of the shortest
// chain from 'beginWord' to 'endWord'
public static int ladderLength(String beginWord, String endWord,
                               ArrayList<String> wordList)
{
 
    /* q1 is used to traverse the graph from beginWord
        and q2 is used to traverse the graph from endWord.
        vis1 and vis2 are used to keep track of the
        visited states from respective directions */
    Queue<node> q1 = new LinkedList<>();
    Queue<node> q2 = new LinkedList<>();
    HashMap<String, Integer> vis1 = new HashMap<>();
    HashMap<String, Integer> vis2 = new HashMap<>();
 
    node start = new node(beginWord, 1);
    node end = new node(endWord, 1);
 
    vis1.put(beginWord, 1);
    q1.add(start);
    vis2.put(endWord, 1);
    q2.add(end);
 
    while (q1.size() > 0 && q2.size() > 0)
    {
 
        // Fetch the current node
        // from the source queue
        node curr1 = q1.remove();
 
        // Fetch the current node from
        // the destination queue
        node curr2 = q2.remove();
 
        // Check all the neighbors of curr1
        for (int i = 0; i < wordList.size(); i++)
        {
 
            // If any one of them is adjacent to curr1
            // and is not present in vis1
            // then push it in the queue
            if (isAdj(curr1.word,wordList.get(i)) &&
                vis1.containsKey(wordList.get(i)) == false)
            {
 
                node temp = new node(wordList.get(i),
                                      curr1.len + 1);
                q1.add(temp);
                vis1.put(wordList.get(i), curr1.len + 1);
 
                // If temp is the destination node
                // then return the answer
                if (temp.word.equals(endWord))
                {
                    return temp.len;
                }
 
                // If temp is present in vis2 i.e. distance from
                // temp and the destination is already calculated
                if (vis2.containsKey(temp.word))
                {
                    return temp.len + vis2.get(temp.word) - 1;
                }
            }
        }
 
        // Check all the neighbors of curr2
        for (int i = 0; i < wordList.size(); i++)
        {
 
            // If any one of them is adjacent to curr2
            // and is not present in vis1 then push it in the queue.
            if (isAdj(curr2.word,wordList.get(i)) &&
                vis2.containsKey(wordList.get(i)) == false)
            {
 
                node temp = new node(wordList.get(i),
                                     curr2.len + 1 );
                q2.add(temp);
                vis2.put(wordList.get(i), curr2.len + 1);
 
                // If temp is the destination node
                // then return the answer
                if (temp.word.equals(beginWord))
                {
                    return temp.len;
                }
 
                // If temp is present in vis1 i.e. distance from
                // temp and the source is already calculated
                if (vis1.containsKey(temp.word))
                {
                    return temp.len + vis1.get(temp.word) - 1;
                }
            }
        }
    }
    return 0;
}
 
// Driver code
public static void main(String args[])
{
    ArrayList<String> wordList = new ArrayList<>();
    wordList.add("poon");
    wordList.add("plee");
    wordList.add("same");
    wordList.add("poie");
    wordList.add("plie");
    wordList.add("poin");
    wordList.add("plea");
 
    String start = "toon";
    String target = "plea";
 
    System.out.println(ladderLength(start, target, wordList));
}
}
 
// This code is contributed by Sambhav Jain




// C# implementation of the approach   
using System;
using System.Collections.Generic;
 
class GFG
{
     
class node
{
    public String word;
    public int len;
    public node(String word, int len)
    {
        this.word = word;
        this.len = len;
    }
}
 
public static bool isAdj(String a, String b)
{
    int count = 0;
    for (int i = 0; i < a.Length; i++)
    {
        if (a[i] != b[i])
            count++;
    }
    if (count == 1)
        return true;
    return false;
}
 
// Function to return the length of the shortest
// chain from 'beginWord' to 'endWord'
public static int ladderLength(String beginWord, String endWord,
                            List<String> wordList)
{
 
    /* q1 is used to traverse the graph from beginWord
        and q2 is used to traverse the graph from endWord.
        vis1 and vis2 are used to keep track of the
        visited states from respective directions */
    Queue<node> q1 = new Queue<node>();
    Queue<node> q2 = new Queue<node>();
    Dictionary<String, int> vis1 = new Dictionary<String, int>();
    Dictionary<String, int> vis2 = new Dictionary<String, int>();
 
    node start = new node(beginWord, 1);
    node end = new node(endWord, 1);
 
    vis1.Add(beginWord, 1);
    q1.Enqueue(start);
    vis2.Add(endWord, 1);
    q2.Enqueue(end);
 
    while (q1.Count > 0 && q2.Count > 0)
    {
 
        // Fetch the current node
        // from the source queue
        node curr1 = q1.Dequeue();
 
        // Fetch the current node from
        // the destination queue
        node curr2 = q2.Dequeue();
 
        // Check all the neighbors of curr1
        for (int i = 0; i < wordList.Count; i++)
        {
 
            // If any one of them is adjacent to curr1
            // and is not present in vis1
            // then push it in the queue
            if (isAdj(curr1.word,wordList[i]) &&
                vis1.ContainsKey(wordList[i]) == false)
            {
 
                node temp = new node(wordList[i],
                                    curr1.len + 1);
                q1.Enqueue(temp);
                vis1.Add(wordList[i], curr1.len + 1);
 
                // If temp is the destination node
                // then return the answer
                if (temp.word.Equals(endWord))
                {
                    return temp.len;
                }
 
                // If temp is present in vis2 i.e. distance from
                // temp and the destination is already calculated
                if (vis2.ContainsKey(temp.word))
                {
                    return temp.len + vis2[temp.word] - 1;
                }
            }
        }
 
        // Check all the neighbors of curr2
        for (int i = 0; i < wordList.Count; i++)
        {
 
            // If any one of them is adjacent to curr2
            // and is not present in vis1 then push it in the queue.
            if (isAdj(curr2.word,wordList[i]) &&
                vis2.ContainsKey(wordList[i]) == false)
            {
 
                node temp = new node(wordList[i],
                                    curr2.len + 1 );
                q2.Enqueue(temp);
                vis2.Add(wordList[i], curr2.len + 1);
 
                // If temp is the destination node
                // then return the answer
                if (temp.word.Equals(beginWord))
                {
                    return temp.len;
                }
 
                // If temp is present in vis1 i.e. distance from
                // temp and the source is already calculated
                if (vis1.ContainsKey(temp.word))
                {
                    return temp.len + vis1[temp.word] - 1;
                }
            }
        }
    }
    return 0;
}
 
// Driver code
public static void Main(String []args)
{
    List<String> wordList = new List<String>();
    wordList.Add("poon");
    wordList.Add("plee");
    wordList.Add("same");
    wordList.Add("poie");
    wordList.Add("plie");
    wordList.Add("poin");
    wordList.Add("plea");
 
    String start = "toon";
    String target = "plea";
 
    Console.WriteLine(ladderLength(start, target, wordList));
}
}
 
// This code is contributed by Rajput-Ji




# Python 3 implementation of the approach
from collections import deque as dq
 
# class for queue
class node :
    def __init__(self, w, l):
        self.word=w
        self.len=l
 
 
# Function that returns true if a and b
# differ in only a single character
def isAdj(a, b):
    count = 0
    for i in range(len(a)):
        if (a[i] != b[i]):
            count+=1
     
    if (count == 1):
        return True
    return False
 
 
# Function to return the length of the shortest
# chain from ‘beginWord’ to ‘endWord’
def ladderLength(beginWord, endWord, wordList):
 
    # q1 is used to traverse the graph from beginWord
    # and q2 is used to traverse the graph from endWord.
    # vis1 and vis2 are used to keep track of the
    # visited states from respective directions
    q1=dq([])
    q2=dq([])
    vis1=dict()
    vis2=dict()
 
    start = node(beginWord, 1)
    end = node(endWord, 1)
 
    vis1[beginWord] = 1
    q1.append(start)
    vis2[endWord] = 1
    q2.append(end)
 
    while (q1 and q2):
 
        # Fetch the current node
        # from the source queue
        curr1 = q1.popleft()
 
        # Fetch the current node from
        # the destination queue
        curr2 = q2.popleft()
 
        # Check all the neighbors of curr1
        for it in wordList:
 
            # If any one of them is adjacent to curr1
            # and is not present in vis1
            # then push it in the queue
            if (isAdj(curr1.word, it) and it not in vis1) :
 
                temp = node(it, curr1.len + 1)
                q1.append(temp)
                vis1[it] = curr1.len + 1
 
                # If temp is the destination node
                # then return the answer
                if (temp.word == endWord) :
                    return temp.len
                 
 
                # If temp is present in vis2 i.e. distance from
                # temp and the destination is already calculated
                if temp.word in vis2 :
                    return temp.len + vis2[temp.word] - 1
                 
             
         
 
        # Check all the neighbors of curr2
        for it in wordList:
 
            # If any one of them is adjacent to curr2
            # and is not present in vis1 then push it in the queue.
            if (isAdj(curr2.word, it) and it not in vis2) :
 
                temp = node(it, curr2.len + 1)
                q2.append(temp)
                vis2[it] = curr2.len + 1
 
                # If temp is the destination node
                # then return the answer
                if (temp.word == beginWord) :
                    return temp.len
                 
 
                # If temp is present in vis1 i.e. distance from
                # temp and the source is already calculated
                if temp.word in vis1:
                    return temp.len + vis1[temp.word] - 1         
 
    return 0
 
 
# Driver code
if __name__=='__main__':
 
    wordList=[]
    wordList.append("poon")
    wordList.append("plee")
    wordList.append("same")
    wordList.append("poie")
    wordList.append("plie")
    wordList.append("poin")
    wordList.append("plea")
 
    start = "toon"
    target = "plea"
 
    print(ladderLength(start, target, wordList))




class Node {
  constructor(w, l) {
    this.word = w;
    this.len = l;
  }
}
// Function that returns true if a and b
// differ in only a single character
 
function isAdj(a, b) {
  let count = 0;
  for (let i = 0; i < a.length; i++) {
    if (a[i] !== b[i]) {
      count++;
    }
  }
 
  if (count === 1) {
    return true;
  }
  return false;
}
//Function to return the length of the shortest
//chain from ‘beginWord’ to ‘endWord
 
function ladderLength(beginWord, endWord, wordList) {
   // q1 is used to traverse the graph from beginWord
    // and q2 is used to traverse the graph from endWord.
    //vis1 and vis2 are used to keep track of the
    //visited states from respective directions
  const q1 = [];
  const q2 = [];
  const vis1 = {};
  const vis2 = {};
 
  const start = new Node(beginWord, 1);
  const end = new Node(endWord, 1);
 
  vis1[beginWord] = 1;
  q1.push(start);
  vis2[endWord] = 1;
  q2.push(end);
 
  while (q1.length > 0 && q2.length > 0) {
      // Fetch the current node
        // from the source queue
    const curr1 = q1.shift();
    const curr2 = q2.shift();
 
    for (const it of wordList) {
      if (isAdj(curr1.word, it) && !vis1[it]) {
        const temp = new Node(it, curr1.len + 1);
        q1.push(temp);
        vis1[it] = curr1.len + 1;
 
        if (temp.word === endWord) {
          return temp.len;
        }
 
        if (temp.word in vis2) {
          return temp.len + vis2[temp.word] - 1;
        }
      }
    }
 
    for (const it of wordList) {
      if (isAdj(curr2.word, it) && !vis2[it]) {
        const temp = new Node(it, curr2.len + 1);
        q2.push(temp);
        vis2[it] = curr2.len + 1;
 
        if (temp.word === beginWord) {
          return temp.len;
        }
 
        if (temp.word in vis1) {
          return temp.len + vis1[temp.word] - 1;
        }
      }
    }
  }
 
  return 0;
}
 
const wordList = [];
wordList.push("poon");
wordList.push("plee");
wordList.push("same");
wordList.push("poie");
wordList.push("plie");
wordList.push("poin");
wordList.push("plea");
 
const start = "toon";
const target = "plea";
 
console.log(ladderLength(start, target, wordList));
 
// This code is contributed by lokeshpotta20.

Output
7

Time Complexity: O(N^2), where N is the length of the string.
Auxiliary Space: O(N). 

Approach 2:

[Printing the sequence itself]

  1. Perform BFS
  2. Now in the BFS, instead of storing the string and level
  3. We now store the sequence itself
  4. We delete the words used in every level at the start of next level.
  5. Hence we get the sequence

 CODE:




// C++ implementation of the approach
#include <bits/stdc++.h>
using namespace std;
 
 
// Function to return the sequences itself
vector<vector<string>> findSequences(string beginWord, string endWord, vector<string>& wordList) {
         
        vector<vector<string>> ans;
        unordered_set<string> vis(wordList.begin(), wordList.end());
        vector<string> UsedOnLvl;
         
        queue<vector<string>> qu;
        qu.push({beginWord});
        int level = 0;
         
        while(!qu.empty()){
            vector<string> vec = qu.front();
            qu.pop();
             
            if(vec.size() > level){
                level++;
                //calc for next lvl is gonna happen
                //lvl 0 1 2 3 4 5
                 
                //also del prev guys used
                //or else infinite loop and not short len (duplication)
                for(auto str : UsedOnLvl){
                    vis.erase(str);
                }
                UsedOnLvl.clear(); // cleared
            }
             
             
            string last = vec.back();
             
            if(last == endWord){
                if(ans.size() == 0){
                    //1st time
                    ans.push_back(vec);
                }
                //we have one or more
                else if(ans[0].size() == vec.size()){
                    //same len then add it
                    ans.push_back(vec);
                }
            }
             
            for(int i=0; i<last.size(); i++){
                char org = last[i];
                for(char ch='a'; ch<='z'; ch++){
                    last[i] = ch;
                    if(vis.find(last)!=vis.end()){
                        //its there
                        vec.push_back(last);
                        qu.push(vec);
                        vec.pop_back();
                        UsedOnLvl.push_back(last); // we dont erase now, we do it before next lvl starts
                    }
                }
                  //done so replace back to org, hit - ait...zit. Now zit to hit back
                last[i] = org;
            }
             
             
        }
        return ans;
}
 
// Driver code
int main()
{
 
    vector<string> wordList;
    wordList.push_back("poon");
    wordList.push_back("plee");
    wordList.push_back("same");
    wordList.push_back("poie");
    wordList.push_back("plie");
    wordList.push_back("poin");
    wordList.push_back("plea");
 
    string start = "toon";
    string target = "plea";
 
    vector<vector<string>> ans = findSequences(start, target, wordList);
      for(auto a : ans){
        for(string s : a){
            cout<<s<<" ";
        }
          cout<<"\n"<<"Length of sequence is "<<a.size()<<"\n";
    }
 
    return 0;
}
//Code done by Balakrishnan R (rbkraj000)




import java.util.*;
 
public class Main {
 
    // Function to return the sequences itself
    static List<List<String>> findSequences(String beginWord, String endWord, List<String> wordList) {
        List<List<String>> ans = new ArrayList<>();
        Set<String> vis = new HashSet<>(wordList);
        List<String> UsedOnLvl = new ArrayList<>();
 
        Queue<List<String>> qu = new LinkedList<>();
        List<String> temp = new ArrayList<>();
        temp.add(beginWord);
        qu.add(temp);
        int level = 0;
 
        while (!qu.isEmpty()) {
            List<String> vec = qu.poll();
 
            if (vec.size() > level) {
                level++;
                //calc for next lvl is gonna happen
                //lvl 0 1 2 3 4 5
 
                //also del prev guys used
                //or else infinite loop and not short len (duplication)
                for (String str : UsedOnLvl) {
                    vis.remove(str);
                }
                UsedOnLvl.clear(); // cleared
            }
 
            String last = vec.get(vec.size() - 1);
 
            if (last.equals(endWord)) {
                if (ans.size() == 0) {
                    //1st time
                    ans.add(vec);
                }
                //we have one or more
                else if (ans.get(0).size() == vec.size()) {
                    //same len then add it
                    ans.add(vec);
                }
            }
 
            for (int i = 0; i < last.length(); i++) {
                char[] arr = last.toCharArray();
                char org = arr[i];
                for (char ch = 'a'; ch <= 'z'; ch++) {
                    arr[i] = ch;
                    String newStr = new String(arr);
                    if (vis.contains(newStr)) {
                        //its there
                        List<String> tempVec = new ArrayList<>(vec);
                        tempVec.add(newStr);
                        qu.add(tempVec);
                        UsedOnLvl.add(newStr); // we dont erase now, we do it before next lvl starts
                    }
                }
                //done so replace back to org, hit - ait...zit. Now zit to hit back
                arr[i] = org;
                last = new String(arr);
            }
        }
        return ans;
    }
 
    // Driver code
    public static void main(String[] args) {
        List<String> wordList = new ArrayList<>();
        wordList.add("poon");
        wordList.add("plee");
        wordList.add("same");
        wordList.add("poie");
        wordList.add("plie");
        wordList.add("poin");
        wordList.add("plea");
 
        String start = "toon";
        String target = "plea";
 
        List<List<String>> ans = findSequences(start, target, wordList);
        for (List<String> a : ans) {
            for (String s : a) {
                System.out.print(s + " ");
            }
            System.out.println("\nLength of sequence is " + a.size());
        }
    }
}




from typing import List
from collections import deque
 
def findSequences(beginWord: str, endWord: str, wordList: List[str]) -> List[List[str]]:
    # Initialize the variables
    ans = []  # list to hold the answer
    vis = set(wordList)  # set to keep track of visited words
    usedOnLvl = []  # list to hold the words used on the current level
    q = deque()  # deque to implement the BFS
    q.append([beginWord])  # start the BFS with the initial word
    level = 0  # current level
     
    # Implement BFS
    while q:
        vec = q.popleft()  # get the first word in the deque
        if len(vec) > level:
            level += 1
            for str in usedOnLvl:
                vis.remove(str# remove the words used on the current level from the visited set
            usedOnLvl = []  # reset the list of words used on the current level
         
        last = vec[-1# get the last word in the sequence
        if last == endWord:
            if not ans:
                ans.append(vec)  # if the answer is empty, add the sequence
            elif len(ans[0]) == len(vec):
                ans.append(vec)  # if the length of the sequence is equal to the first sequence in the answer, add the sequence
         
        for i in range(len(last)):
            org = last[i]  # store the original character
            for c in 'abcdefghijklmnopqrstuvwxyz':
                last = last[:i] + c + last[i+1:]  # replace the current character with a new character
                if last in vis:
                    vec.append(last)  # add the new word to the sequence
                    q.append(vec.copy())  # add the new sequence to the deque
                    vec.pop()  # remove the new word from the sequence
                    usedOnLvl.append(last)  # add the new word to the list of words used on the current level
            last = last[:i] + org + last[i+1:]  # restore the original character
     
    return ans  # return the answer
 
 
# Test the function with the given example
wordList = ["poon", "plee", "same", "poie", "plie", "poin", "plea"]
start = "toon"
target = "plea"
ans = findSequences(start, target, wordList)
for a in ans:
    print(*a, f"\nLength of sequence is {len(a)}\n")




using System;
using System.Collections.Generic;
 
class Program
{
  // Function to return the sequences itself
  static List<List<string>> findSequences(string beginWord,
                                          string endWord,
                                          List<string> wordList)
  {
    List<List<string>> ans = new List<List<string>>();
    HashSet<string> vis = new HashSet<string>(wordList);
    List<string> UsedOnLvl = new List<string>();
    Queue<List<string>> qu = new Queue<List<string>>();
    qu.Enqueue(new List<string> { beginWord });
    int level = 0;
 
    while (qu.Count > 0)
    {
      List<string> vec = qu.Dequeue();
      if (vec.Count > level)
      {
        level++;
 
        //calc for next lvl is gonna happen
        //lvl 0 1 2 3 4 5
 
        //also del prev guys used
        //or else infinite loop and not short len (duplication)
        for (int i = 0; i < UsedOnLvl.Count; i++)
        {
          vis.Remove(UsedOnLvl[i]);
        }
        UsedOnLvl.Clear();
      }
      string last = vec[vec.Count - 1];
      if (last == endWord)
      {
        if (ans.Count == 0)
        {
          ans.Add(vec);
        }
        else if (ans[0].Count == vec.Count)
        {
          ans.Add(vec);
        }
      }
      for (int i = 0; i < last.Length; i++)
      {
        char org = last[i];
        for (char ch = 'a'; ch <= 'z'; ch++)
        {
          last = last.Substring(0, i) + ch + last.Substring(i + 1);
          if (vis.Contains(last))
          {
            vec.Add(last);
            qu.Enqueue(new List<string>(vec));
            vec.RemoveAt(vec.Count - 1);
            UsedOnLvl.Add(last);
          }
        }
        last = last.Substring(0, i) + org + last.Substring(i + 1);
      }
    }
    return ans;
  }
 
  static void Main(string[] args)
  {
    List<string> wordList = new List<string>();
    wordList.Add("poon");
    wordList.Add("plee");
    wordList.Add("same");
    wordList.Add("poie");
    wordList.Add("plie");
    wordList.Add("poin");
    wordList.Add("plea");
 
    string start = "toon";
    string target = "plea";
 
    List<List<string>> ans = findSequences(start, target, wordList);
    foreach (List<string> a in ans)
    {
      foreach (string s in a)
      {
        Console.Write(s + " ");
      }
      Console.WriteLine("\nLength of sequence is " + a.Count);
    }
  }
}




// Javascript program for the above approach
 
function findSequences(beginWord, endWord, wordList) {
  let ans = []; // list to hold the answer
  let vis = new Set(wordList); // set to keep track of visited words
  let usedOnLvl = []; // list to hold the words used on the current level
  let q = []; // array to implement the BFS
  q.push([beginWord]); // start the BFS with the initial word
  let level = 0; // current level
 
  // Implement BFS
  while (q.length) {
    let vec = q.shift(); // get the first word in the array
    if (vec.length > level) {
      level += 1;
      for (let str of usedOnLvl) {
        vis.delete(str); // remove the words used on the current level from the visited set
      }
      usedOnLvl = []; // reset the list of words used on the current level
    }
 
    let last = vec[vec.length - 1]; // get the last word in the sequence
    if (last == endWord) {
      if (!ans.length) {
        ans.push(vec); // if the answer is empty, add the sequence
      } else if (ans[0].length == vec.length) {
        ans.push(vec); // if the length of the sequence is equal to the first sequence in the answer, add the sequence
      }
    }
 
    for (let i = 0; i < last.length; i++) {
      let org = last[i]; // store the original character
      for (let c of 'abcdefghijklmnopqrstuvwxyz') {
        last = last.slice(0, i) + c + last.slice(i + 1); // replace the current character with a new character
        if (vis.has(last)) {
          vec.push(last); // add the new word to the sequence
          q.push(vec.slice()); // add the new sequence to the array
          vec.pop(); // remove the new word from the sequence
          usedOnLvl.push(last); // add the new word to the list of words used on the current level
        }
      }
      last = last.slice(0, i) + org + last.slice(i + 1); // restore the original character
    }
  }
 
  return ans; // return the answer
}
 
// Test the function with the given example
let wordList = ["poon", "plee", "same", "poie", "plie", "poin", "plea"];
let start = "toon";
let target = "plea";
let ans = findSequences(start, target, wordList);
for (let a of ans) {
  console.log(...a, `\nLength of sequence is ${a.length}\n`);
}
 
// This code is contributed by princekumaras

Output
toon poon poin poie plie plee plea 
Length of sequence is 7

Time Complexity: O(N*M*26)

Auxiliary Space: O(N*M)

where N = length of wordList and M = | wordList[i] |


Article Tags :