953. Verifying an Alien Dictionary
Problem Description
You're given words from an alien language that uses the same 26 lowercase English letters (a-z
), but in a potentially different alphabetical order. The aliens have their own custom ordering of these letters.
Your task is to determine whether a given list of words is sorted according to the alien dictionary order.
Input:
words
: A list of strings containing words written in the alien languageorder
: A string of 26 characters representing the alien alphabetical order (a permutation ofa-z
)
Output:
- Return
true
if the words are sorted lexicographically according to the alien order - Return
false
otherwise
Example:
If the alien order is "hlabcdefgijkmnopqrstuvwxyz"
(where h
comes before l
, which comes before a
, etc.), then:
["hello", "leetcode"]
would be sorted correctly becauseh
comes beforel
in the alien order["word", "world"]
would need to be checked character by character using the alien order
The solution creates a mapping of each character to its position in the alien alphabet, then compares words character by character up to position 20 (assuming maximum word length). For each position i
, it checks if all words maintain the correct order at that character position. If at any position the ordering is violated (a previous character has a higher order value than the current one), it returns false
. If all characters at a position are different and properly ordered, the words are confirmed to be sorted.
Intuition
To check if words are sorted in an alien dictionary, we need to compare them the same way we'd compare words in a regular dictionary - character by character from left to right. The key insight is that we need to translate the alien ordering into something we can work with numerically.
Think about how you check if words are sorted in English: you compare the first letters, and if they're the same, you move to the second letters, and so on. The difference here is that instead of using the standard a < b < c...
ordering, we have a custom ordering defined by the order
string.
The natural approach is to create a mapping where each character gets assigned its position in the alien alphabet. For example, if order = "bac..."
, then b
has position 0, a
has position 1, and c
has position 2. This way, we can compare characters numerically - a character with a smaller position value comes before one with a larger position value.
The solution iterates through each character position (column-wise) across all words. At position i
, it extracts the i
-th character from each word and checks if these characters are in non-decreasing order according to the alien alphabet. If we find any position where a word has a character that comes after the corresponding character in the previous word (higher position value), the list isn't sorted.
The clever part is handling words of different lengths - when a word doesn't have a character at position i
(it's too short), we treat it as having value -1
, which naturally comes before any actual character. This handles cases like "app"
coming before "apple"
correctly.
The algorithm stops early when it finds a position where all words have different characters that are properly ordered (the valid
flag), confirming the sort order without needing to check further positions.
Solution Approach
The solution implements a column-wise comparison strategy to verify the alien dictionary ordering:
Step 1: Create Character-to-Position Mapping
m = {c: i for i, c in enumerate(order)}
This creates a dictionary where each character maps to its position in the alien alphabet. For example, if order = "bac..."
, then m = {'b': 0, 'a': 1, 'c': 2, ...}
.
Step 2: Column-wise Iteration The algorithm iterates through character positions 0 to 19 (assuming maximum word length of 20):
for i in range(20):
Step 3: Character Comparison at Each Position
For each position i
, the algorithm:
- Initializes
prev = -1
to track the previous word's character value - Sets
valid = True
to check if all characters at this position are distinct and ordered
Step 4: Process Each Word
for x in words:
curr = -1 if i >= len(x) else m[x[i]]
For each word, it extracts the character at position i
:
- If the word is too short (
i >= len(x)
), assign-1
as the value - Otherwise, get the character's position value from the mapping
m[x[i]]
Step 5: Order Validation
if prev > curr: return False if prev == curr: valid = False prev = curr
- If
prev > curr
: The previous word's character comes after the current word's character in the alien order, so the list is not sorted - returnFalse
- If
prev == curr
: The characters are the same, so we need to check the next position - setvalid = False
- Update
prev
for the next iteration
Step 6: Early Termination
if valid: return True
If valid
remains True
after checking all words at position i
, it means all characters at this position are different and in the correct order. The words are confirmed to be sorted, so return True
.
Step 7: Default Return
If the loop completes without finding a definitive answer (all positions checked with ties), return True
as the words are considered sorted.
This approach efficiently handles edge cases like words of different lengths and ensures lexicographic ordering according to the alien alphabet.
Ready to land your dream job?
Unlock your dream job with a 3-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through the algorithm with a concrete example:
Input:
words = ["cat", "car", "card"]
order = "cabdefghijklmnopqrstuvwxyz"
(where 'c' comes first, then 'a', then 'b', etc.)
Step 1: Create mapping
m = {'c': 0, 'a': 1, 'b': 2, 'd': 3, ..., 't': 19, 'r': 17, ...}
Step 2: Check position i = 0 (first character)
- Word "cat": character 'c' → m['c'] = 0
- Word "car": character 'c' → m['c'] = 0
- Word "card": character 'c' → m['c'] = 0
Comparison sequence:
- prev = -1 (initial)
- "cat": curr = 0, prev (-1) ≤ curr (0) ✓, prev = curr so valid = False
- "car": curr = 0, prev (0) ≤ curr (0) ✓, prev = curr so valid = False
- "card": curr = 0, prev (0) ≤ curr (0) ✓, prev = curr so valid = False
All first characters are the same, so valid = False, continue to next position.
Step 3: Check position i = 1 (second character)
- Word "cat": character 'a' → m['a'] = 1
- Word "car": character 'a' → m['a'] = 1
- Word "card": character 'a' → m['a'] = 1
Comparison sequence:
- prev = -1 (reset)
- "cat": curr = 1, prev (-1) ≤ curr (1) ✓, prev = curr so valid = False
- "car": curr = 1, prev (1) ≤ curr (1) ✓, prev = curr so valid = False
- "card": curr = 1, prev (1) ≤ curr (1) ✓, prev = curr so valid = False
All second characters are the same, continue to next position.
Step 4: Check position i = 2 (third character)
- Word "cat": character 't' → m['t'] = 19
- Word "car": character 'r' → m['r'] = 17
- Word "card": character 'r' → m['r'] = 17
Comparison sequence:
- prev = -1 (reset)
- "cat": curr = 19, prev (-1) ≤ curr (19) ✓, update prev = 19
- "car": curr = 17, prev (19) > curr (17) ✗
Since 19 > 17, the character 't' comes after 'r' in the alien order, but "cat" appears before "car" in our list. This violates the sorting order.
Result: Return False
The words are not sorted according to the alien dictionary because "cat" should come after "car" (since 't' comes after 'r' in the alien alphabet).
Solution Implementation
1class Solution:
2 def isAlienSorted(self, words: List[str], order: str) -> bool:
3 # Create a mapping from each character to its position in the alien alphabet
4 char_to_position = {char: position for position, char in enumerate(order)}
5
6 # Check each position (column) across all words, up to position 20
7 for position_index in range(20):
8 previous_char_value = -1 # Initialize with -1 (smaller than any valid position)
9 all_chars_different = True # Track if all characters at this position are different
10
11 # Check each word at the current position
12 for word in words:
13 # Get character value at current position (-1 if position exceeds word length)
14 if position_index >= len(word):
15 current_char_value = -1 # Treat end of word as smallest value
16 else:
17 current_char_value = char_to_position[word[position_index]]
18
19 # If previous character has larger value, words are not sorted
20 if previous_char_value > current_char_value:
21 return False
22
23 # If characters are equal, we need to check next position
24 if previous_char_value == current_char_value:
25 all_chars_different = False
26
27 previous_char_value = current_char_value
28
29 # If all characters at this position are different, words are sorted
30 if all_chars_different:
31 return True
32
33 # After checking 20 positions, assume words are sorted
34 return True
35
1class Solution {
2 public boolean isAlienSorted(String[] words, String order) {
3 // Create a mapping array to store the position of each character in the alien order
4 // orderMap[char - 'a'] = position of char in the alien alphabet
5 int[] orderMap = new int[26];
6 for (int i = 0; i < 26; i++) {
7 orderMap[order.charAt(i) - 'a'] = i;
8 }
9
10 // Check each column position across all words (column-by-column comparison)
11 // Maximum 20 iterations is sufficient for typical constraints
12 for (int columnIndex = 0; columnIndex < 20; columnIndex++) {
13 int previousCharOrder = -1;
14 boolean allCharsDifferent = true;
15
16 // Compare characters at the current column position for all words
17 for (String word : words) {
18 // Get the order value of character at current position
19 // If position exceeds word length, use -1 (treats shorter words as smaller)
20 int currentCharOrder = columnIndex >= word.length() ?
21 -1 : orderMap[word.charAt(columnIndex) - 'a'];
22
23 // If previous character has higher order than current, words are not sorted
24 if (previousCharOrder > currentCharOrder) {
25 return false;
26 }
27
28 // Track if all characters in this column are different
29 if (previousCharOrder == currentCharOrder) {
30 allCharsDifferent = false;
31 }
32
33 previousCharOrder = currentCharOrder;
34 }
35
36 // If all characters in this column are different and in order,
37 // the words are sorted (no need to check further columns)
38 if (allCharsDifferent) {
39 break;
40 }
41 }
42
43 return true;
44 }
45}
46
1class Solution {
2public:
3 bool isAlienSorted(vector<string>& words, string order) {
4 // Create a mapping from alien character to its position in the alien alphabet
5 // charToPosition[c] represents the position of character c in the alien order
6 vector<int> charToPosition(26);
7 for (int i = 0; i < 26; ++i) {
8 charToPosition[order[i] - 'a'] = i;
9 }
10
11 // Check character by character at each position (up to position 20)
12 // This assumes the maximum word length is 20
13 for (int position = 0; position < 20; ++position) {
14 int prevCharPosition = -1; // Position of previous word's character in alien order
15 bool allDifferent = true; // Flag to check if all characters at current position are different
16
17 // Iterate through all words and compare characters at current position
18 for (const auto& word : words) {
19 // Get the alien order position of current character
20 // If position exceeds word length, use -1 (represents empty character)
21 int currCharPosition = (position >= word.size()) ? -1 : charToPosition[word[position] - 'a'];
22
23 // If previous character comes after current in alien order, words are not sorted
24 if (prevCharPosition > currCharPosition) {
25 return false;
26 }
27
28 // If characters are same, we need to check next position
29 if (prevCharPosition == currCharPosition) {
30 allDifferent = false;
31 }
32
33 prevCharPosition = currCharPosition;
34 }
35
36 // If all characters at this position are different and in order,
37 // the words are sorted (no need to check further positions)
38 if (allDifferent) {
39 break;
40 }
41 }
42
43 return true;
44 }
45};
46
1/**
2 * Verifies if words are sorted according to an alien dictionary order
3 * @param words - Array of words to check if they are in lexicographical order
4 * @param order - String representing the alien alphabet order
5 * @returns true if words are sorted according to the alien order, false otherwise
6 */
7function isAlienSorted(words: string[], order: string): boolean {
8 // Create a map to store character positions in the alien alphabet
9 const charToOrderMap = new Map<string, number>();
10
11 // Map each character to its position in the alien alphabet
12 for (const char of order) {
13 charToOrderMap.set(char, charToOrderMap.size);
14 }
15
16 const wordCount = words.length;
17
18 // Compare each adjacent pair of words
19 for (let i = 1; i < wordCount; i++) {
20 const previousWord = words[i - 1];
21 const currentWord = words[i];
22 const minLength = Math.min(previousWord.length, currentWord.length);
23 let foundDifference = false;
24
25 // Compare characters at each position
26 for (let j = 0; j < minLength; j++) {
27 const previousCharOrder = charToOrderMap.get(previousWord[j])!;
28 const currentCharOrder = charToOrderMap.get(currentWord[j])!;
29
30 // If previous word's character comes after current word's character, not sorted
31 if (previousCharOrder > currentCharOrder) {
32 return false;
33 }
34
35 // If we found a character that determines the order, mark and break
36 if (previousCharOrder < currentCharOrder) {
37 foundDifference = true;
38 break;
39 }
40 }
41
42 // If all compared characters are equal but previous word is longer, not sorted
43 // Example: "apple" should not come before "app"
44 if (!foundDifference && previousWord.length > currentWord.length) {
45 return false;
46 }
47 }
48
49 return true;
50}
51
Time and Space Complexity
Time Complexity: O(n * m)
where n
is the total number of characters across all words and m
is the length of the alphabet (order string).
- Creating the mapping dictionary
m
takesO(m)
time wherem
is the length of the order string (26 for the alphabet). - The outer loop runs at most 20 times (constant).
- The inner loop iterates through all words, which is
O(w)
wherew
is the number of words. - For each word, we access at most one character and perform a dictionary lookup, which is
O(1)
. - In the worst case, we examine all characters across all words before returning, giving us
O(n)
wheren
is the total number of characters. - Since the outer loop is bounded by a constant (20), the overall time complexity is
O(n + m)
, which simplifies toO(n)
whenn >> m
.
However, a more precise analysis shows that we iterate through positions (up to 20) and for each position check all words, so it's O(min(20, maxLength) * w)
where w
is the number of words and maxLength
is the length of the longest word. This can be simplified to O(n)
where n
is the total number of characters across all words.
Space Complexity: O(m)
where m
is the length of the order string.
- The mapping dictionary
m
stores each character in the order string with its index, requiringO(m)
space. - The variables
prev
,curr
, andvalid
use constant spaceO(1)
. - No additional data structures are created that scale with input size.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Incorrect Handling of Words with Different Lengths
A critical pitfall in this solution is the assumption that shorter words should always come before longer words when one is a prefix of the other. The code assigns -1
to positions beyond a word's length, which makes shorter words automatically "smaller" than longer words at those positions.
Problem Example:
- Alien order:
"abcdefghijklmnopqrstuvwxyz"
(standard order) - Words:
["apple", "app"]
- Expected:
False
(because "apple" should come after "app" in standard lexicographic order) - Current code returns:
True
(incorrectly)
Why it fails: When comparing at position 3:
- "apple" has character 'l' with value 11
- "app" has no character (assigned -1)
- Since 11 > -1, the code thinks "apple" > "app", which seems correct
- But the issue is that the code continues checking and never properly validates that "app" should come first
2. Fixed Position Limit of 20
The hardcoded limit of 20 positions assumes no word will be longer than 20 characters. If words exceed this length and differ only after position 20, the algorithm will incorrectly return True
.
Solution:
class Solution:
def isAlienSorted(self, words: List[str], order: str) -> bool:
# Create character-to-position mapping
char_to_position = {char: position for position, char in enumerate(order)}
# Compare adjacent words pairwise
for i in range(len(words) - 1):
word1, word2 = words[i], words[i + 1]
# Compare characters one by one
for j in range(len(word1)):
# If word2 is shorter than word1 and is a prefix, wrong order
if j >= len(word2):
return False
# Get character values in alien order
char1_value = char_to_position[word1[j]]
char2_value = char_to_position[word2[j]]
# If char1 comes after char2 in alien order, wrong order
if char1_value > char2_value:
return False
# If char1 comes before char2, this pair is valid
elif char1_value < char2_value:
break
# If equal, continue to next character
return True
Key Improvements:
- Pairwise comparison: Instead of column-wise checking, compare adjacent words directly
- Proper prefix handling: Explicitly check if a longer word comes before its prefix
- No arbitrary length limit: Works for words of any length
- Clearer logic: Each word pair is validated completely before moving to the next
Which of these properties could exist for a graph but not a tree?
Recommended Readings
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Runtime Overview When learning about algorithms and data structures you'll frequently encounter the term time complexity This concept is fundamental in computer science and offers insights into how long an algorithm takes to complete given a certain input size What is Time Complexity Time complexity represents the amount of time
Want a Structured Path to Master System Design Too? Don’t Miss This!