Open In App

Alien Dictionary

Last Updated : 14 Apr, 2025
Comments
Improve
Suggest changes
Like Article
Like
Report

Given an array of strings words[], sorted in an alien language. Your task is to determine the correct order of letters in this alien language based on the given words. If the order is valid, return a string containing the unique letters in lexicographically increasing order as per the new language's rules.

Note: There can be multiple valid orders for a given input, so you may return any one of them. If no valid order exists due to conflicting constraints, return an empty string.

Examples:  

Input: words[] = ["baa", "abcd", "abca", "cab", "cad"]
Output: "bdac"
Explanation:
The pair “baa” and “abcd” suggests ‘b’ appears before ‘a’ in the alien dictionary.
The pair “abcd” and “abca” suggests ‘d’ appears before ‘a’ in the alien dictionary.
The pair “abca” and “cab” suggests ‘a’ appears before ‘c’ in the alien dictionary.
The pair “cab” and “cad” suggests ‘b’ appears before ‘d’ in the alien dictionary.
So, ‘b’→'d’ →‘a’ →‘c’ is a valid ordering.

Input: words[] = ["caa", "aaa", "aab"]
Output: "cab"
Explanation: The pair "caa" and "aaa" suggests 'c' appears before 'a'.
The pair "aaa" and "aab" suggests 'a' appear before 'b' in the alien dictionary.
So, 'c' → 'a' → 'b' is a valid ordering.

[Approach 1] Using Kahn's algorithm

Kahn’s Algorithm is ideal for this problem because we are required to determine the order of characters in an alien language based on a sorted dictionary. This naturally forms a Directed Acyclic Graph (DAG) where an edge u -> v means character u comes before character v.

Kahn's Algorithm is a BFS-based topological sort, which efficiently determines a valid linear order of nodes (characters) in a DAG. It’s particularly useful here because it handles dependencies using in-degrees, making it simple to identify characters with no prerequisites and process them in correct order. It also easily detects cycles, if a valid topological sort is not possible (e.g., cyclic dependency), it returns early.

Step-by-Step Implementation

  • Initialize an adjacency list graph[26], in-degree array inDegree[26], and existence tracker exists[26].
  • Mark characters that appear in the input words using the exists array.
  • Compare each adjacent pair of words to find the first differing character.
  • Add a directed edge from the first different character of the first word to the second word’s, and increment in-degree of the latter.
  • Check for invalid prefix cases where a longer word appears before its prefix (e.g., "abc" before "ab").
  • Push all characters with in-degree 0 into a queue as starting nodes for BFS.
  • Pop from the queue, add to result, and reduce in-degree of neighbors.
  • Enqueue neighbors whose in-degree becomes 0 after reduction.
  • Detect cycles by checking if all existing characters were processed (length of result).
  • Return the result string if no cycle is detected; otherwise, return an empty string.
C++
#include <iostream>
#include <vector>
#include <queue>
#include <string>
using namespace std;

// Function to find the order of characters in alien dictionary 
string findOrder(const vector<string>& words) {
    
    // Adjacency list
    vector<vector<int>> graph(26);     
    
    // In-degree of each character
    vector<int> inDegree(26, 0);       
    
    // Tracks which characters are present
    vector<bool> exists(26, false);    

    // Mark existing characters
    for (const string& word : words) {
        for (char ch : word) {
            exists[ch - 'a'] = true;
        }
    }

    // Build the graph from adjacent words
    for (int i = 0; i + 1 < words.size(); ++i) {
        const string& w1 = words[i];
        const string& w2 = words[i + 1];
        int len = min(w1.length(), w2.length());
        int j = 0;

        while (j < len && w1[j] == w2[j]) ++j;

        if (j < len) {
            int u = w1[j] - 'a';
            int v = w2[j] - 'a';
            graph[u].push_back(v);
            inDegree[v]++;
        } else if (w1.size() > w2.size()) {
            
            // Invalid input 
            return "";
        }
    }

    // Topological sort 
    queue<int> q;
    for (int i = 0; i < 26; ++i) {
        if (exists[i] && inDegree[i] == 0) {
            q.push(i);
        }
    }

    string result;
    while (!q.empty()) {
        int u = q.front(); q.pop();
        result += (char)(u + 'a');

        for (int v : graph[u]) {
            inDegree[v]--;
            if (inDegree[v] == 0) {
                q.push(v);
            }
        }
    }

    // Check, there was a cycle or not
    for (int i = 0; i < 26; ++i) {
        if (exists[i] && inDegree[i] != 0) {
            return "";
        }
    }

    return result;
}

// Driver code
int main() {
    vector<string> words = {"baa", "abcd", "abca", "cab", "cad"};
    string order = findOrder(words);

    cout<<order;
    return 0;
}
Java
import java.util.*;

class GfG{

    // Function to find the order of characters in alien dictionary 
    public static String findOrder(String[] words) {
        
        ArrayList<ArrayList<Integer>> graph = new ArrayList<>();
        int[] inDegree = new int[26];
        boolean[] exists = new boolean[26];

        // Initialize graph
        for (int i = 0; i < 26; i++) {
            graph.add(new ArrayList<>());
        }

        // Mark characters that exist in the input
        for (String word : words) {
            for (char ch : word.toCharArray()) {
                exists[ch - 'a'] = true;
            }
        }

        // Build the graph based on adjacent word comparisons
        for (int i = 0; i < words.length - 1; i++) {
            String w1 = words[i];
            String w2 = words[i + 1];
            int len = Math.min(w1.length(), w2.length());
            int j = 0;

            while (j < len && w1.charAt(j) == w2.charAt(j)) {
                j++;
            }

            if (j < len) {
                int u = w1.charAt(j) - 'a';
                int v = w2.charAt(j) - 'a';
                graph.get(u).add(v);
                inDegree[v]++;
            } else if (w1.length() > w2.length()) {
                
                // Invalid input
                return "";
            }
        }

        // Topological Sort
        Queue<Integer> q = new LinkedList<>();
        for (int i = 0; i < 26; i++) {
            if (exists[i] && inDegree[i] == 0) {
                q.offer(i);
            }
        }

        StringBuilder result = new StringBuilder();

        while (!q.isEmpty()) {
            int u = q.poll();
            result.append((char) (u + 'a'));

            for (int v : graph.get(u)) {
                inDegree[v]--;
                if (inDegree[v] == 0) {
                    q.offer(v);
                }
            }
        }

        // Check for cycle
        for (int i = 0; i < 26; i++) {
            if (exists[i] && inDegree[i] != 0) {
                return "";
            }
        }

        return result.toString();
    }

    public static void main(String[] args) {
        
        String[] words = {"baa", "abcd", "abca", "cab", "cad"};
        String order = findOrder(words);

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

def findOrder(words):
    
    # Adjacency list
    graph = [[] for _ in range(26)]     
    
    # In-degree array
    inDegree = [0] * 26 
    
    # To track which letters exist in input
    exists = [False] * 26

    # Mark existing characters 
    for word in words:
        for ch in word:
            exists[ord(ch) - ord('a')] = True

    # Build graph 
    for i in range(len(words) - 1):
        w1, w2 = words[i], words[i + 1]
        minlen = min(len(w1), len(w2))
        j = 0
        while j < minlen and w1[j] == w2[j]:
            j += 1

        if j < minlen:
            u = ord(w1[j]) - ord('a')
            v = ord(w2[j]) - ord('a')
            graph[u].append(v)
            inDegree[v] += 1
        elif len(w1) > len(w2):
            # Invalid case
            return ""  

    # Topological sort
    q = deque([i for i in range(26) if exists[i] 
                                 and inDegree[i] == 0])
    result = []

    while q:
        u = q.popleft()
        result.append(chr(u + ord('a')))
        for v in graph[u]:
            inDegree[v] -= 1
            if inDegree[v] == 0:
                q.append(v)

    if len(result) != sum(exists):
        
        # Cycle detected or incomplete order
        return ""  

    return ''.join(result)

# Example usage
words = ["baa", "abcd", "abca", "cab", "cad"]
order = findOrder(words)

print(order)
C#
using System;
using System.Collections.Generic;

class GfG {
    
    public static string findOrder(string[] words) {
        
        List<List<int>> graph = new List<List<int>>();
        int[] inDegree = new int[26];
        bool[] exists = new bool[26];

        // Initialize graph
        for (int i = 0; i < 26; i++) {
            graph.Add(new List<int>());
        }

        // Mark existing characters
        foreach (string word in words) {
            foreach (char ch in word) {
                exists[ch - 'a'] = true;
            }
        }

        // Build the graph 
        for (int i = 0; i + 1 < words.Length; i++) {
            string w1 = words[i];
            string w2 = words[i + 1];
            int len = Math.Min(w1.Length, w2.Length);
            int j = 0;

            while (j < len && w1[j] == w2[j]) j++;

            if (j < len) {
                int u = w1[j] - 'a';
                int v = w2[j] - 'a';
                graph[u].Add(v);
                inDegree[v]++;
            } else if (w1.Length > w2.Length) {
                return "";
            }
        }

        // Topological Sort
        Queue<int> q = new Queue<int>();
        for (int i = 0; i < 26; i++) {
            if (exists[i] && inDegree[i] == 0) {
                q.Enqueue(i);
            }
        }

        string result = "";
        while (q.Count > 0) {
            int u = q.Dequeue();
            result += (char)(u + 'a');

            foreach (int v in graph[u]) {
                inDegree[v]--;
                if (inDegree[v] == 0) {
                    q.Enqueue(v);
                }
            }
        }

        // Check for cycle
        for (int i = 0; i < 26; i++) {
            if (exists[i] && inDegree[i] != 0) {
                return "";
            }
        }

        return result;
    }

    public static void Main() {
        string[] words = {"baa", "abcd", "abca", "cab", "cad"};
        string order = findOrder(words);
        
        Console.WriteLine(order);
    }
}
JavaScript
function findOrder(words) {
    
    const graph = Array.from({ length: 26 }, () => []);
    const inDegree = Array(26).fill(0);
    const exists = Array(26).fill(false);

    // Mark existing characters
    for (const word of words) {
        for (const ch of word) {
            exists[ch.charCodeAt(0) - 97] = true;
        }
    }

    // Build the graph
    for (let i = 0; i < words.length - 1; i++) {
        const w1 = words[i], w2 = words[i + 1];
        let j = 0;
        while (j < Math.min(w1.length, w2.length) && w1[j] === w2[j]) {
            j++;
        }

        if (j < w1.length && j < w2.length) {
            const u = w1.charCodeAt(j) - 97;
            const v = w2.charCodeAt(j) - 97;
            graph[u].push(v);
            inDegree[v]++;
        } else if (w1.length > w2.length) {
            
            // Invalid input
            return ""; 
        }
    }

    // Topological Sort (BFS)
    const q = [];
    for (let i = 0; i < 26; i++) {
        if (exists[i] && inDegree[i] === 0) {
            q.push(i);
        }
    }

    const result = [];

    while (q.length > 0) {
        const u = q.shift();
        result.push(String.fromCharCode(u + 97));
        for (const v of graph[u]) {
            inDegree[v]--;
            if (inDegree[v] === 0) {
                q.push(v);
            }
        }
    }

    if (result.length !== exists.filter(x => x).length) {
        
        // Cycle detected
        return ""; 
    }

    return result.join('');
}

// Example usage
const words = ["baa", "abcd", "abca", "cab", "cad"];
const order = findOrder(words);
console.log(order);

Output
bdac

Time Complexity: O(n * m) , where n is size of array arr[], m is the size of each string arr[i]
Auxiliary Space: O(1)

DFS (Depth-First Search) helps us find the correct order of letters in the alien language. By looking at how the words are ordered in the alien dictionary, we can figure out which letter should come before another. DFS is useful here because it lets us go through the letters step by step, following these rules, and helps us build the final order correctly.

DFS works well because it can detect cycles in the graph (which would make a valid ordering impossible) and enables us to build the correct order of characters as we traverse the graph. Once we visit all dependencies of a character (all the characters it must come before), we can add it to the result. The DFS ensures we are not violating any character order constraints while visiting nodes and is an effective way to perform a topological sort.

C++
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>

using namespace std;

// Depth-first search function for topological sorting and cycle detection
bool dfs(int u, vector<vector<int>> &graph, vector<int> &vis, 
                                    vector<int> &rec, string &ans) {
    
    // Mark the node as visited and part of the current recursion stack
    vis[u] = rec[u] = 1;  

    for (int v = 0; v < 26; v++) {
        if (graph[u][v]) {  
            if (!vis[v]) {  
                // Recurse and check for cycle
                if (!dfs(v, graph, vis, rec, ans))  
                    return false;
            } else if (rec[v]) {
                 // A cycle is detected if v is already in the current recursion stack
                return false; 
            }
        }
    }
    // Add the character to the result after visiting all dependencies
    ans.push_back(char('a' + u));
    // Remove from recursion stack
    rec[u] = 0;  
    return true;
}

// Function to find the correct order of characters in an alien dictionary
string findOrder(vector<string> &words) {
    
    // Adjacency matrix for character precedence
    vector<vector<int>> graph(26, vector<int>(26, 0));
    // Marks if a character exists in the dictionary
    vector<int> exist(26, 0);  
    // Marks if a character has been visited
    vector<int> vis(26, 0);   
    // Recursion stack to detect cycles
    vector<int> rec(26, 0);   
    // Resultant character order
    string ans = "";          

    // Step 1: Mark all characters that appear in the input
    for (string word : words) {
        for (char ch : word) {
            exist[ch - 'a'] = 1;
        }
    }

    //Build the graph 
    for (int i = 0; i + 1 < words.size(); i++) {
        const string &a = words[i], &b = words[i + 1];
        int n = a.size(), m = b.size(), ind = 0;

        // Find the first different character between a and b
        while (ind < n && ind < m && a[ind] == b[ind])
            ind++;

        if (ind != n && ind == m)
            return "";

        
        if (ind < n && ind < m)
            graph[a[ind] - 'a'][b[ind] - 'a'] = 1;
    }

    
    for (int i = 0; i < 26; i++) {
        if (exist[i] && !vis[i]) {
            if (!dfs(i, graph, vis, rec, ans)) {
                // Return empty string if a cycle is found
                return "";  
            }
        }
    }
    // Reverse to get the correct topological order
    reverse(ans.begin(), ans.end());  
    return ans;
}

// Driver code to test the implementation
int main() {
    vector<string> words = {"baa", "abcd", "abca", "cab", "cad"};

    string order = findOrder(words);

    if (order.empty()) {
        cout << "No Ordering Possible" << endl;
    } else {
        cout << order << endl;
    }

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

class GfG {

    // Depth-first search function for topological sorting 
    // and cycle detection
    static boolean dfs(int u, int[][] graph, int[] vis, int[] rec, 
                                          StringBuilder ans) {
        
        // Mark as visited and add to recursion stack
        vis[u] = rec[u] = 1; 

        for (int v = 0; v < 26; v++) {
            if (graph[u][v] == 1) {
                if (vis[v] == 0) {
                    if (!dfs(v, graph, vis, rec, ans))
                        return false;
                } else if (rec[v] == 1) {
                    
                    // Cycle detected
                    return false;
                }
            }
        }
        // Append after visiting dependencies
        ans.append((char) ('a' + u));
        
        // Remove from recursion stack
        rec[u] = 0; 
        return true;
    }

    // Function to find the correct order of characters 
    // in an alien dictionary
    static String findOrder(String[] words) {
        int[][] graph = new int[26][26];
        int[] exist = new int[26];
        int[] vis = new int[26];
        int[] rec = new int[26];
        StringBuilder ans = new StringBuilder();

        // Mark characters that appear
        for (String word : words) {
            for (int j = 0; j < word.length(); j++) {
                char ch = word.charAt(j);
                exist[ch - 'a'] = 1;
            }
        }


        // Build the graph
        for (int i = 0; i + 1 < words.length; i++) {
            String a = words[i], b = words[i + 1];
            int n = a.length(), m = b.length(), ind = 0;

            while (ind < n && ind < m && a.charAt(ind) == b.charAt(ind))
                ind++;

            if (ind != n && ind == m)
                // Invalid case
                return ""; 

            if (ind < n && ind < m)
                graph[a.charAt(ind) - 'a'][b.charAt(ind) - 'a'] = 1;
        }

        for (int i = 0; i < 26; i++) {
            if (exist[i] == 1 && vis[i] == 0) {
                if (!dfs(i, graph, vis, rec, ans)) {
                    return "";
                }
            }
        }
        // Reverse to get correct order
        return ans.reverse().toString(); 
    }

    public static void main(String[] args) {
        String[] words = {"baa", "abcd", "abca", "cab", "cad"};
        String order = findOrder(words);

        if (order.isEmpty()) {
            System.out.println("No Ordering Possible");
        } else {
            System.out.println(order);
        }
    }
}
Python
def dfs(u, graph, vis, rec, ans):
    # Mark the node as visited and part of the current recursion stack
    vis[u] = rec[u] = 1

    for v in range(26):
        if graph[u][v]:
            if not vis[v]:
                # Recurse and check for cycle
                if not dfs(v, graph, vis, rec, ans):
                    return False
            elif rec[v]:
                # A cycle is detected
                return False

    # Add character after visiting dependencies
    ans.append(chr(ord('a') + u))
    rec[u] = 0  # Remove from recursion stack
    return True

def findOrder(words):
    # 26x26 adjacency matrix
    graph = [[0 for _ in range(26)] for _ in range(26)]
    exist = [0] * 26
    vis = [0] * 26
    rec = [0] * 26
    ans = []

    # Step 1: Mark all characters that appear
    for word in words:
        for ch in word:
            exist[ord(ch) - ord('a')] = 1

    # Step 2: Build graph
    for i in range(len(words) - 1):
        a, b = words[i], words[i + 1]
        n, m = len(a), len(b)
        ind = 0

        while ind < n and ind < m and a[ind] == b[ind]:
            ind += 1

        if ind != n and ind == m:
            return ""  # Invalid case

        if ind < n and ind < m:
            graph[ord(a[ind]) - ord('a')][ord(b[ind]) - ord('a')] = 1

    # Step 3: DFS
    for i in range(26):
        if exist[i] and not vis[i]:
            if not dfs(i, graph, vis, rec, ans):
                return ""

    return ''.join(reversed(ans))


# Driver code
words = ["baa", "abcd", "abca", "cab", "cad"]
order = findOrder(words)

if not order:
    print("No Ordering Possible")
else:
    print(order)
C#
using System;
using System.Collections.Generic;
using System.Text;

class GfG{

    // Depth-first search function for topological sorting and 
    // cycle detection
    static bool dfs(int u, int[,] graph, int[] vis, int[] rec, 
                                  StringBuilder ans) {
        // Mark visited and add to recursion stack
        vis[u] = rec[u] = 1; 

        for (int v = 0; v < 26; v++) {
            if (graph[u, v] == 1) {
                if (vis[v] == 0) {
                    if (!dfs(v, graph, vis, rec, ans))
                        return false;
                } else if (rec[v] == 1) {
                    // Cycle detected
                    return false;
                }
            }
        }
        // Add after dependencies
        ans.Append((char)('a' + u));
        // Remove from recursion stack
        rec[u] = 0; 
        return true;
    }

    // Function to find the correct order of characters
    // in an alien dictionary
    static string findOrder(List<string> words) {
        int[,] graph = new int[26, 26];
        int[] exist = new int[26];
        int[] vis = new int[26];
        int[] rec = new int[26];
        StringBuilder ans = new StringBuilder();

        // Mark characters that appear
        foreach (string word in words) {
            foreach (char ch in word) {
                exist[ch - 'a'] = 1;
            }
        }

        // Build graph
        for (int i = 0; i + 1 < words.Count; i++) {
            string a = words[i], b = words[i + 1];
            int n = a.Length, m = b.Length, ind = 0;

            while (ind < n && ind < m && a[ind] == b[ind])
                ind++;

            if (ind != n && ind == m)
                // Invalid case
                return ""; 

            if (ind < n && ind < m)
                graph[a[ind] - 'a', b[ind] - 'a'] = 1;
        }

        for (int i = 0; i < 26; i++) {
            if (exist[i] == 1 && vis[i] == 0) {
                if (!dfs(i, graph, vis, rec, ans)) {
                    return "";
                }
            }
        }

        // Reverse to get the correct topological order
        char[] charArray = ans.ToString().ToCharArray();
        Array.Reverse(charArray);
        
        return new string(charArray);
    }

    static void Main() {
        List<string> words = new List<string>
                        { "baa", "abcd", "abca", "cab", "cad" };
        string order = findOrder(words);

        if (order == "") {
            Console.WriteLine("No Ordering Possible");
        } else {
            Console.WriteLine(order);
        }
    }
}
JavaScript
function dfs(u, graph, vis, rec, ans) {
    
     // Mark visited and recursion stack
    vis[u] = rec[u] = 1; 

    for (let v = 0; v < 26; v++) {
        if (graph[u][v]) {
            if (!vis[v]) {
                if (!dfs(v, graph, vis, rec, ans))
                    return false;
            } else if (rec[v]) {
                // Cycle detected
                return false;
            }
        }
    }

    ans.push(String.fromCharCode(97 + u));  
    rec[u] = 0;
    return true;
}

function findOrder(words) {
    
    let graph = Array.from({ length: 26 }, () => Array(26).fill(0));
    let exist = Array(26).fill(0);
    let vis = Array(26).fill(0);
    let rec = Array(26).fill(0);
    let ans = [];

    // Mark existing characters
    for (let word of words) {
        for (let i = 0; i < word.length; i++) {
            exist[word.charCodeAt(i) - 97] = 1;
        }
    }

    // Build graph
    for (let i = 0; i + 1 < words.length; i++) {
        let a = words[i];
        let b = words[i + 1];
        let n = a.length, m = b.length, ind = 0;

        while (ind < n && ind < m && a[ind] === b[ind])
            ind++;

        if (ind !== n && ind === m)
            // Invalid
            return "";  

        if (ind < n && ind < m)
            graph[a.charCodeAt(ind) - 97][b.charCodeAt(ind) - 97] = 1;
    }

    for (let i = 0; i < 26; i++) {
        if (exist[i] && !vis[i]) {
            if (!dfs(i, graph, vis, rec, ans))
                return "";
        }
    }

    return ans.reverse().join('');
}

// Driver code
let words = ["baa", "abcd", "abca", "cab", "cad"];
let order = findOrder(words);

if (order === "") {
    console.log("No Ordering Possible");
} else {
    console.log(order);
}

Output
bdac

Time Complexity: O(n * m) , where n is size of array arr[], m is the size of each string arr[i]
Auxiliary Space: O(1)



Next Article

Similar Reads