Alien Dictionary
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 characteru
comes before characterv
.
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 arrayinDegree[26]
, and existence trackerexists[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.
#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;
}
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);
}
}
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)
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);
}
}
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)
[Approach 2] Using Depth-First Search
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.
#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;
}
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);
}
}
}
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)
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);
}
}
}
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)