Alien Dictionary

Given a sorted dictionary of an alien language having N words and k starting alphabets of standard dictionary. Find the order of characters in the alien language.
Note: Many orders may be possible for a particular test case, thus you may return any valid order and output will be 1 if the order of string returned by the function is correct else 0 denoting incorrect string returned.
 

Examples :

Input:  N = 5, K = 4, dict = {"baa","abcd","abca","cab","cad"}
Output: 1
Explanation: Here order of characters is 'b', 'd', 'a', 'c' Note that words are sorted and in the given language "baa" comes before "abcd", therefore 'b' is before 'a' in output.
Similarly we can find other orders.
Input: N = 3, K = 3, dict = {"caa","aaa","aab"}
Output: 1
Explanation: Here order of characters is 'c', 'a', 'b' Note that words are sorted and in the given language "caa" comes before "aaa", therefore 'c' is before 'a' in output.
Similarly we can find other orders.

Expected Time Complexity: O(N * |S| + K) , where |S| denotes maximum length.
Expected Space Compelxity: O(K)

Constraints:
1 ≤ N≤ 104
1 ≤ K ≤ 26
1 ≤ Length of words ≤ 50


Approach 01:

#include <vector>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <algorithm>
using namespace std;

class Solution {
public:
    string findOrder(string alienDictionary[], int numWords, int numCharacters) {
        // Create an adjacency list for the graph where each character maps to a set of characters it precedes
        unordered_map<char, unordered_set<char>> adjacencyList;
        for (int i = 0; i < numWords; ++i) {
            for (char c : alienDictionary[i]) {
                adjacencyList[c]; // Ensure each character is in the adjacency list
            }
        }
        
        // Build the graph based on the given order of words
        for (int i = 0; i < numWords - 1; ++i) {
            const string& word1 = alienDictionary[i];
            const string& word2 = alienDictionary[i + 1];
            int minLength = min(word1.length(), word2.length());
            
            // Check for invalid input where word1 is longer but the prefix is the same
            if (word1.length() > word2.length() && word1.substr(0, minLength) == word2.substr(0, minLength)) {
                return ""; // Invalid ordering
            }
            
            // Find the first different character and update the adjacency list
            for (int j = 0; j < minLength; ++j) {
                if (word1[j] != word2[j]) {
                    adjacencyList[word1[j]].insert(word2[j]);
                    break;
                }
            }
        }
        
        // Set to track visited nodes and vector to store the topological order
        unordered_set<char> visited;
        vector<char> topologicalOrder;
        
        // Perform topological sort using DFS
        for (const auto& pair : adjacencyList) {
            char charNode = pair.first;
            if (visited.find(charNode) == visited.end()) {
                performTopologicalSort(charNode, visited, topologicalOrder, adjacencyList);
            }
        }
        
        // Return the reversed topological order to get the correct character order
        reverse(topologicalOrder.begin(), topologicalOrder.end());
        return string(topologicalOrder.begin(), topologicalOrder.end());
    }

private:
    // Perform a DFS-based topological sort
    void performTopologicalSort(char node, unordered_set<char>& visited, vector<char>& topologicalOrder,
                                 const unordered_map<char, unordered_set<char>>& adjacencyList) {
        if (visited.find(node) != visited.end()) {
            return;
        }
        
        visited.insert(node);
        for (char neighbor : adjacencyList.at(node)) {
            performTopologicalSort(neighbor, visited, topologicalOrder, adjacencyList);
        }
        
        topologicalOrder.push_back(node);
    }
};
from typing import List

class Solution:
    def findOrder(self, alienDictionary, numWords, numCharacters):
        # Create a graph with each character mapping to a set of characters it comes before
        adjacencyList = {char: set() for word in alienDictionary for char in word}
        
        # Build the graph based on the given order of words
        for i in range(numWords - 1):
            word1, word2 = alienDictionary[i], alienDictionary[i + 1]
            minLength = min(len(word1), len(word2))
            
            # Check for invalid input where word1 is longer but the prefix is the same
            if len(word1) > len(word2) and word1[:minLength] == word2[:minLength]:
                return ""
            
            # Find the first different character and build the adjacency list
            for j in range(minLength):
                if word1[j] != word2[j]:
                    adjacencyList[word1[j]].add(word2[j])
                    break
        
        # Set to track visited nodes and list to store the topological order
        visited = set()
        topologicalOrder = []
    
        def topologicalSort(node):
            if node in visited:
                return True
            
            visited.add(node)
            for neighbor in adjacencyList[node]:
                topologicalSort(neighbor)
            
            topologicalOrder.append(node)
            return True
        
        # Perform topological sort for each character in the graph
        for char in adjacencyList:
            topologicalSort(char)
        
        # Return the reversed topological order to get the correct character order
        return topologicalOrder[::-1]

Time Complexity

  • Building the Adjacency List:

    Constructing the adjacency list takes \( O(\text{numEdges}) \) time, as we iterate through each edge and add it to the list.

  • Breadth-First Search (BFS):

    BFS traverses each node and edge once. The time complexity of BFS is \( O(\text{numNodes} + \text{numEdges}) \), where \( \text{numNodes} \) is the number of nodes and \( \text{numEdges} \) is the number of edges.

  • Overall Time Complexity:

    The overall time complexity is \( O(\text{numNodes} + \text{numEdges}) \), combining the time to build the adjacency list and perform BFS.

Space Complexity

  • Adjacency List:

    The adjacency list requires \( O(\text{numNodes} + \text{numEdges}) \) space. Each node’s adjacency list stores all its edges, summing up to the total number of edges.

  • Distance Array:

    The distance array requires \( O(\text{numNodes}) \) space to store the shortest path distances for each node.

  • Queue:

    The queue used in BFS can hold up to \( O(\text{numNodes}) \) nodes at a time in the worst case.

  • Overall Space Complexity:

    The overall space complexity is \( O(\text{numNodes} + \text{numEdges}) \), combining the space for the adjacency list, distance array, and BFS queue.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top