Skip to content

Commit 0ae6830

Browse files
committed
Add problem 269
1 parent f7afadf commit 0ae6830

File tree

2 files changed

+111
-0
lines changed

2 files changed

+111
-0
lines changed

problems/graph/alien_dictionary.py

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
from collections import deque
2+
from typing import List
3+
4+
5+
class AlienDictionary:
6+
@staticmethod
7+
def foreignDictionary(words: List[str]) -> str:
8+
# Total number of words
9+
n = len(words)
10+
# Graph to represent the order among characters
11+
graph = [[False] * 26 for _ in range(26)]
12+
# Characters that are present in the words
13+
characters = [False] * 26
14+
# Count of unique characters in the dictionary
15+
unique_character_count = 0
16+
# Process the words to fill up characters array
17+
for word in words:
18+
for c in word:
19+
if unique_character_count == 26:
20+
break
21+
index = ord(c) - ord('a')
22+
if not characters[index]:
23+
characters[index] = True
24+
unique_character_count += 1
25+
26+
# Populate the graph
27+
for i in range(n - 1):
28+
for j in range(len(words[i])):
29+
# If next word is a prefix of current word then
30+
# we have an invalid order
31+
if j >= len(words[i + 1]):
32+
return ""
33+
# Compare characters of two consecutive words
34+
current_character, next_character = words[i][j], words[i + 1][j]
35+
# If these characters are same then we don't get any information
36+
# about the order
37+
if current_character == next_character:
38+
continue
39+
x, y = ord(current_character) - ord('a'), ord(next_character) - ord('a')
40+
# If there is already a directed edge between next_character
41+
# and current_character, then we have a cycle in the graph
42+
if graph[y][x]:
43+
return ""
44+
# Create an edge between current_character and next_character
45+
graph[x][y] = True
46+
break
47+
48+
# Since the graph is created, we will perform topological sorting
49+
50+
# 1. Array to store indegrees of nodes
51+
indegrees = [0] * 26
52+
for i in range(26):
53+
for j in range(26):
54+
if i != j and characters[i] and characters[j] and graph[i][j]:
55+
indegrees[j] += 1
56+
57+
# 2. Queue to store nodes with zero indegree
58+
nodes = deque()
59+
for i in range(26):
60+
if characters[i] and indegrees[i] == 0:
61+
nodes.append(i)
62+
63+
# 3. Perform topological sorting
64+
order = []
65+
while nodes:
66+
current = nodes.popleft()
67+
order.append(chr(current + ord('a')))
68+
for i in range(26):
69+
if i != current and characters[i] and graph[current][i]:
70+
indegrees[i] -= 1
71+
if indegrees[i] == 0:
72+
nodes.append(i)
73+
74+
return "" if len(order) < unique_character_count else "".join(order)
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import unittest
2+
3+
from problems.graph.alien_dictionary import AlienDictionary
4+
5+
6+
class TestAlienDictionary(unittest.TestCase):
7+
8+
def setUp(self):
9+
self.alien_dictionary = AlienDictionary()
10+
11+
def test_valid_order(self):
12+
words = ["wrt", "wrf", "er", "ett", "rftt"]
13+
self.assertEqual(self.alien_dictionary.foreignDictionary(words), "wertf")
14+
15+
def test_invalid_order_due_to_cycle(self):
16+
words = ["z", "x", "z"]
17+
self.assertEqual(self.alien_dictionary.foreignDictionary(words), "")
18+
19+
def test_invalid_order_due_to_prefix(self):
20+
words = ["abc", "ab"]
21+
self.assertEqual(self.alien_dictionary.foreignDictionary(words), "")
22+
23+
def test_single_word(self):
24+
words = ["abc"]
25+
self.assertEqual(self.alien_dictionary.foreignDictionary(words), "abc")
26+
27+
def test_empty_words(self):
28+
words = []
29+
self.assertEqual(self.alien_dictionary.foreignDictionary(words), "")
30+
31+
def test_no_valid_order(self):
32+
words = ["a", "b", "c"]
33+
self.assertEqual(self.alien_dictionary.foreignDictionary(words), "abc")
34+
35+
36+
if __name__ == '__main__':
37+
unittest.main()

0 commit comments

Comments
 (0)