Shortest Transformation Length in a Word List

Last Updated : 26 Jun, 2026

Given two distinct words s and e, and a list of unique words words[], where all words have the same length, find the length of the shortest transformation sequences from start to end. 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 the length of the shortest transformation sequence from s to e. If no such sequence exists, return 0.

Examples:

Input: words[] = ["des", "der", "dfr", "dgt", "dfs"], start = "der", end = "dfs"
Output: 3
Explanation: The length of the smallest transformation sequence from "der" to "dfs" is 3 i.e "der" -> "dfr" -> "dfs".

Input: words[] = ["geek", "gefk"], start = "gedk", end = "geek"
Output: 2
Explanation: The length of the smallest transformation sequence from "gedk" to "geek" is 2 i.e "gedk" -> "geek".

Input: words[] = ["poon", "plee", "same", "poie", "plea", "plie", "poin"], start = "toon", end = "plea"
Output:
Explanation: The length of the smallest transformation sequence from "toon" to "plea" is 7 i.e toon -> poon -> poin -> poie -> plie -> plee -> plea.

Try It Yourself
redirect icon

[Naive Approach] Backtracking Over All Possible Sequences - O(n!) Time and O(n) Space

The idea is to recursively try every possible transformation sequence starting from start. From the current word, move to every unvisited word that differs by exactly one character and keep track of the minimum sequence length that reaches end.

C++
#include <bits/stdc++.h>
using namespace std;

// Recursive function to find the shortest transformation chain
int minWordTransform(string start, string target, map<string, int> &mp)
{

    // If start word is the same as target, no transformation is needed
    if (start == target)
        return 1;

    int mini = INT_MAX;

    // Mark current word as visited
    mp[start] = 1;

    // Try changing each character of the word
    for (int i = 0; i < start.size(); i++)
    {
        char originalChar = start[i];

        // Try all possible lowercase letters at position i
        for (char ch = 'a'; ch <= 'z'; ch++)
        {
            start[i] = ch;

            // If the new word exists in dictionary and is not visited
            if (mp.find(start) != mp.end() && mp[start] == 0)
            {

                // Recursive call for next transformation
                int curr = minWordTransform(start, target, mp);

                if (curr != INT_MAX)
                    mini = min(mini, 1 + curr);
            }
        }

        // Restore original character before moving to the next position
        start[i] = originalChar;
    }

    // Mark current word as unvisited (backtracking)
    mp[start] = 0;

    return mini;
}

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

    // Initialize all words from the dictionary as unvisited
    map<string, int> mp;

    for (auto word : words)
        mp[word] = 0;

    int result = minWordTransform(s, e, mp);

    if (result == INT_MAX)
        result = 0;

    return result;
}

int main()
{

    vector<string> words = {"poon", "plee", "same", "poie", "plea", "plie", "poin"};

    string s = "toon";
    string e = "plea";

    cout << wordLadder(words, s, e);

    return 0;
}
Java
import java.util.*;

public class GFG {

    // Recursive function to find the shortest
    // transformation chain
    static int minWordTransform(String start, String target,
                                HashMap<String, Integer> mp)
    {

        // If start word is the same as target, no
        // transformation is needed
        if (start.equals(target))
            return 1;

        int mini = Integer.MAX_VALUE;

        // Mark current word as visited
        mp.put(start, 1);

        // Try changing each character of the word
        for (int i = 0; i < start.length(); i++) {
            char originalChar = start.charAt(i);

            // Try all possible lowercase letters at
            // position i
            for (char ch = 'a'; ch <= 'z'; ch++) {
                char[] startChars = start.toCharArray();
                startChars[i] = ch;
                String newStart = new String(startChars);

                // If the new word exists in dictionary and
                // is not visited
                if (mp.containsKey(newStart)
                    && mp.get(newStart) == 0) {

                    // Recursive call for next
                    // transformation
                    int curr = minWordTransform(newStart,
                                                target, mp);

                    if (curr != Integer.MAX_VALUE)
                        mini = Math.min(mini, 1 + curr);
                }
            }

            // Restore original character before moving to
            // the next position
            start = start.substring(0, i) + originalChar
                    + start.substring(i + 1);
        }

        // Mark current word as unvisited (backtracking)
        mp.put(start, 0);

        return mini;
    }

    static int wordLadder(String[] words, String s,
                          String e)
    {

        // Initialize all words from the dictionary as
        // unvisited
        HashMap<String, Integer> mp = new HashMap<>();

        for (String word : words)
            mp.put(word, 0);

        int result = minWordTransform(s, e, mp);

        if (result == Integer.MAX_VALUE)
            result = 0;

        return result;
    }

    public static void main(String[] args)
    {

        String[] words = { "poon", "plee", "same", "poie",
                           "plea", "plie", "poin" };

        String s = "toon";
        String e = "plea";

        System.out.println(wordLadder(words, s, e));
    }
}
Python
def minWordTransform(start, target, mp):

    # If start word is the same as target, no transformation is needed
    if start == target:
        return 1

    mini = float('inf')

    # Mark current word as visited
    mp[start] = 1

    # Try changing each character of the word
    for i in range(len(start)):
        originalChar = start[i]

        # Try all possible lowercase letters at position i
        for ch in range(ord('a'), ord('z') + 1):
            newStart = start[:i] + chr(ch) + start[i + 1:]

            # If the new word exists in dictionary and is not visited
            if newStart in mp and mp[newStart] == 0:

                # Recursive call for next transformation
                curr = minWordTransform(newStart, target, mp)

                if curr != float('inf'):
                    mini = min(mini, 1 + curr)

        # Restore original character before moving to the next position
        start = start[:i] + originalChar + start[i + 1:]

    # Mark current word as unvisited (backtracking)
    mp[start] = 0

    return mini


def wordLadder(words, s, e):

    # Initialize all words from the dictionary as unvisited
    mp = {word: 0 for word in words}

    result = minWordTransform(s, e, mp)

    if result == float('inf'):
        result = 0

    return result


if __name__ == '__main__':
    words = ["poon", "plee", "same", "poie", "plea", "plie", "poin"]
    s = "toon"
    e = "plea"
    print(wordLadder(words, s, e))
C#
using System;
using System.Collections.Generic;

class GFG {
    // Recursive function to find the shortest
    // transformation chain
    public static int
    MinWordTransform(string start, string target,
                     Dictionary<string, int> mp)
    {
        // If start word is the same as target, no
        // transformation is needed
        if (start == target)
            return 1;

        int mini = int.MaxValue;

        // Mark current word as visited
        mp[start] = 1;

        // Try changing each character of the word
        for (int i = 0; i < start.Length; i++) {
            char originalChar = start[i];

            // Try all possible lowercase letters at
            // position i
            for (char ch = 'a'; ch <= 'z'; ch++) {
                char[] startChars = start.ToCharArray();
                startChars[i] = ch;
                string newStart = new string(startChars);

                // If the new word exists in dictionary and
                // is not visited
                if (mp.ContainsKey(newStart)
                    && mp[newStart] == 0) {
                    // Recursive call for next
                    // transformation
                    int curr = MinWordTransform(newStart,
                                                target, mp);

                    if (curr != int.MaxValue)
                        mini = Math.Min(mini, 1 + curr);
                }
            }

            // Restore original character before moving to
            // the next position
            start = start.Substring(0, i) + originalChar
                    + start.Substring(i + 1);
        }

        // Mark current word as unvisited (backtracking)
        mp[start] = 0;

        return mini;
    }

    public static int WordLadder(string[] words, string s,
                                 string e)
    {
        // Initialize all words from the dictionary as
        // unvisited
        Dictionary<string, int> mp
            = new Dictionary<string, int>();

        foreach(string word in words) mp[word] = 0;

        int result = MinWordTransform(s, e, mp);

        if (result == int.MaxValue)
            result = 0;

        return result;
    }

    public static void Main(string[] args)
    {
        string[] words = { "poon", "plee", "same", "poie",
                           "plea", "plie", "poin" };

        string s = "toon";
        string e = "plea";

        Console.WriteLine(WordLadder(words, s, e));
    }
}
JavaScript
function minWordTransform(start, target, mp) {

    // If start word is the same as target, no transformation is needed
    if (start === target)
        return 1;

    let mini = Number.MAX_SAFE_INTEGER;

    // Mark current word as visited
    mp[start] = 1;

    // Try changing each character of the word
    for (let i = 0; i < start.length; i++) {
        let originalChar = start[i];

        // Try all possible lowercase letters at position i
        for (let ch = 'a'.charCodeAt(0); ch <= 'z'.charCodeAt(0); ch++) {
            let newStart = start.slice(0, i) + String.fromCharCode(ch) + start.slice(i + 1);

            // If the new word exists in dictionary and is not visited
            if (mp[newStart]!== undefined && mp[newStart] === 0) {

                // Recursive call for next transformation
                let curr = minWordTransform(newStart, target, mp);

                if (curr!== Number.MAX_SAFE_INTEGER)
                    mini = Math.min(mini, 1 + curr);
            }
        }

        // Restore original character before moving to the next position
        start = start.slice(0, i) + originalChar + start.slice(i + 1);
    }

    // Mark current word as unvisited (backtracking)
    mp[start] = 0;

    return mini;
}

function wordLadder(words, s, e) {

    // Initialize all words from the dictionary as unvisited
    let mp = {};

    for (let word of words)
        mp[word] = 0;

    let result = minWordTransform(s, e, mp);

    if (result === Number.MAX_SAFE_INTEGER)
        result = 0;

    return result;
}

let words = ["poon", "plee", "same", "poie", "plea", "plie", "poin"];
let s = "toon";
let e = "plea";
console.log(wordLadder(words, s, e));

Output
7

[Expected Approach] BFS with Hash Set - O(n m) Time and O(n) Space

The idea is to use BFS to find the smallest chain between start and target. To do so, create a queue words to store the word to visit and push start initially. At each level, go through all the elements stored in queue words, and for each element, alter all of its character for 'a' to 'z' and one by one and check if the new word is in dictionary or not. If found, push the new word in queue, else continue. Each level of queue defines the length of chain, and once the target is found return the value of that level + 1.

Let us understand with example:
Input: words[] = ["poon", "plee", "same", "poie", "plea", "plie", "poin"], start = "toon", end = "plea"

  • Insert all words into a hash set and start BFS from "toon" with sequence length 1.
  • From "toon", changing one character at a time generates "poon", which exists in the dictionary. Push ("poon", 2) into the queue and remove it from the set.
  • Next, process "poon". A valid transformation "poin" is found. Push ("poin", 3) into the queue and remove it from the set.
  • Similarly, BFS discovers the sequence: toon -> poon -> poin -> poie -> plie -> plee -> plea
  • When "plea" is reached, its sequence length is 7. Therefore, the shortest transformation sequence length is 7.
C++
#include <bits/stdc++.h>
using namespace std;

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

    // set to keep track of unvisited words
    unordered_set<string> st(words.begin(), words.end());

    // store the current chain length
    int res = 0;
    int m = s.length();

    // queue to store words to visit
    queue<string> q;

    q.push(s);

    while (!q.empty())
    {
        res++;
        int len = q.size();

        // iterate through all words at same level
        for (int i = 0; i < len; ++i)
        {
            string word = q.front();
            q.pop();

            // For every character of the word
            for (int j = 0; j < m; ++j)
            {

                // Retain the original character
                // at the current position
                char ch = word[j];

                // Replace the current character with
                // every possible lowercase alphabet
                for (char c = 'a'; c <= 'z'; ++c)
                {
                    word[j] = c;

                    // skip the word if already added
                    // or not present in set
                    if (st.find(word) == st.end())
                        continue;

                    // If target word is found
                    if (word == e)
                        return res + 1;

                    // remove the word from set
                    st.erase(word);

                    // And push the newly generated word
                    // which will be a part of the chain
                    q.push(word);
                }

                // Restore the original character
                // at the current position
                word[j] = ch;
            }
        }
    }

    return 0;
}

int main()
{

    vector<string> words = {"poon", "plee", "same", "poie", "plea", "plie", "poin"};

    string s = "toon";
    string e = "plea";

    cout << wordLadder(words, s, e);

    return 0;
}
Java
import java.util.*;

public class GFG {

    public static int wordLadder(String[] words, String s,
                                 String e)
    {

        // set to keep track of unvisited words
        Set<String> st
            = new HashSet<>(Arrays.asList(words));

        // store the current chain length
        int res = 0;
        int m = s.length();

        // queue to store words to visit
        Queue<String> q = new LinkedList<>();

        q.add(s);

        while (!q.isEmpty()) {
            res++;
            int len = q.size();

            // iterate through all words at same level
            for (int i = 0; i < len; ++i) {
                String word = q.poll();

                // For every character of the word
                for (int j = 0; j < m; ++j) {

                    // Retain the original character
                    // at the current position
                    char ch = word.charAt(j);

                    // Replace the current character with
                    // every possible lowercase alphabet
                    char[] wordArray = word.toCharArray();
                    for (char c = 'a'; c <= 'z'; ++c) {
                        wordArray[j] = c;
                        String newWord
                            = new String(wordArray);

                        // skip the word if already added
                        // or not present in set
                        if (!st.contains(newWord))
                            continue;

                        // If target word is found
                        if (newWord.equals(e))
                            return res + 1;

                        // remove the word from set
                        st.remove(newWord);

                        // And push the newly generated word
                        // which will be a part of the chain
                        // which will be a part of the chain
                        q.add(newWord);
                    }

                    // Restore the original character
                    // at the current position
                    wordArray[j] = ch;
                }
            }
        }

        return 0;
    }

    public static void main(String[] args)
    {

        String[] words = { "poon", "plee", "same", "poie",
                           "plea", "plie", "poin" };

        String s = "toon";
        String e = "plea";

        System.out.println(wordLadder(words, s, e));
    }
}
Python
from collections import deque


def wordLadder(words, s, e):
    # set to keep track of unvisited words
    st = set(words)

    # store the current chain length
    res = 0
    m = len(s)

    # queue to store words to visit
    q = deque([s])

    while q:
        res += 1
        len_q = len(q)

        # iterate through all words at same level
        for _ in range(len_q):
            word = q.popleft()

            # For every character of the word
            for j in range(m):
                # Retain the original character
                # at the current position
                ch = word[j]

                # Replace the current character with
                # every possible lowercase alphabet
                for c in range(ord('a'), ord('z') + 1):
                    new_word = word[:j] + chr(c) + word[j + 1:]

                    # skip the word if already added
                    # or not present in set
                    if new_word not in st:
                        continue

                    # If target word is found
                    if new_word == e:
                        return res + 1

                    # remove the word from set
                    st.remove(new_word)

                    # And push the newly generated word
                    # which will be a part of the chain
                    q.append(new_word)

                # Restore the original character
                # at the current position
                word = word[:j] + ch + word[j + 1:]

    return 0


if __name__ == '__main__':
    words = ["poon", "plee", "same", "poie", "plea", "plie", "poin"]
    s = "toon"
    e = "plea"
    print(wordLadder(words, s, e))
C#
using System;
using System.Collections.Generic;

public class GFG {
    public static int wordLadder(string[] words, string s,
                                 string e)
    {
        // set to keep track of unvisited words
        HashSet<string> st = new HashSet<string>(words);

        // store the current chain length
        int res = 0;
        int m = s.Length;

        // queue to store words to visit
        Queue<string> q = new Queue<string>();

        q.Enqueue(s);

        while (q.Count > 0) {
            res++;
            int len = q.Count;

            // iterate through all words at same level
            for (int i = 0; i < len; ++i) {
                string word = q.Dequeue();

                // For every character of the word
                for (int j = 0; j < m; ++j) {
                    // Retain the original character
                    // at the current position
                    char ch = word[j];

                    // Replace the current character with
                    // every possible lowercase alphabet
                    for (char c = 'a'; c <= 'z'; ++c) {
                        char[] wordArray
                            = word.ToCharArray();
                        wordArray[j] = c;
                        string newWord
                            = new string(wordArray);

                        // skip the word if already added
                        // or not present in set
                        if (!st.Contains(newWord))
                            continue;

                        // If target word is found
                        if (newWord == e)
                            return res + 1;

                        // remove the word from set
                        st.Remove(newWord);

                        // And push the newly generated word
                        // which will be a part of the chain
                        q.Enqueue(newWord);
                    }

                    // Restore the original character
                    // at the current position
                    word = word.Substring(0, j) + ch
                           + word.Substring(j + 1);
                }
            }
        }

        return 0;
    }

    public static void Main()
    {
        string[] words = { "poon", "plee", "same", "poie",
                           "plea", "plie", "poin" };

        string s = "toon";
        string e = "plea";

        Console.WriteLine(wordLadder(words, s, e));
    }
}
JavaScript
function wordLadder(words, s, e) {
    // set to keep track of unvisited words
    let st = new Set(words);

    // store the current chain length
    let res = 0;
    let m = s.length;

    // queue to store words to visit
    let q = [s];

    while (q.length > 0) {
        res++;
        let len = q.length;

        // iterate through all words at same level
        for (let i = 0; i < len; ++i) {
            let word = q.shift();

            // For every character of the word
            for (let j = 0; j < m; ++j) {

                // Retain the original character
                // at the current position
                let ch = word[j];

                // Replace the current character with
                // every possible lowercase alphabet
                for (let c = 'a'.charCodeAt(0); c <= 'z'.charCodeAt(0); ++c) {
                    let newWord = word.substring(0, j) + String.fromCharCode(c) + word.substring(j + 1);

                    // skip the word if already added
                    // or not present in set
                    if (!st.has(newWord))
                        continue;

                    // If target word is found
                    if (newWord === e)
                        return res + 1;

                    // remove the word from set
                    st.delete(newWord);

                    // And push the newly generated word
                    // which will be a part of the chain
                    q.push(newWord);
                }

                // Restore the original character
                // at the current position
                word = word.substring(0, j) + ch + word.substring(j + 1);
            }
        }
    }

    return 0;
}

let words = ["poon", "plee", "same", "poie", "plea", "plie", "poin"];
let s = "toon";
let e = "plea";
console.log(wordLadder(words, s, e));

Output
7
Comment