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: 7
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++

filter_none

edit
close

play_arrow

link
brightness_4
code

// 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;
}

chevron_right


Java

filter_none

edit
close

play_arrow

link
brightness_4
code

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

chevron_right


C#

filter_none

edit
close

play_arrow

link
brightness_4
code

// 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

chevron_right


Output:

7

Attention reader! Don’t stop learning now. Get hold of all the important DSA concepts with the DSA Self Paced Course at a student-friendly price and become industry ready.




My Personal Notes arrow_drop_up

Check out this Author's contributed articles.

If you like GeeksforGeeks and would like to contribute, you can also write an article using contribute.geeksforgeeks.org or mail your article to contribute@geeksforgeeks.org. See your article appearing on the GeeksforGeeks main page and help other Geeks.

Please Improve this article if you find anything incorrect by clicking on the "Improve Article" button below.



Improved By : sambhavjain3, Rajput-Ji