alien_dictionary

  1from collections import Counter, defaultdict, deque
  2
  3
  4# @leet start
  5class Solution:
  6    def alienOrder(self, words: list[str]) -> str:
  7        """
  8        This question asks us to return an ordering of characters given a set
  9        of sorted words.
 10
 11        First, we describe the relation between the characters. We do this by
 12        extracting the first character of each pair that doesn't match, and
 13        noting that the left side char comes before the right side.
 14
 15        For these words:
 16
 17        wrt
 18        wrf
 19        er
 20        ett
 21        rftt
 22
 23        the pairs would be (wrt, wrf), (wrf, er), (er, ett), (ett, rftt).
 24
 25        For each pair, we want to find which ones come before, so:
 26
 27        (wrt, wrf) = t -> f (since wr are the same, t and f are the first differing
 28        characters, and t is on the left side of our pair, so t comes before f).
 29        (wrf, er) = w -> e (since w and e don't match, and w is on the left).
 30        (er, ett) = r -> t (since e matches for both, and r is on the left).
 31        (ett, rftt) = e -> r (since e must come before r).
 32
 33        As well, we want to create an indegree map that counts how man indegrees
 34        each node has. For this graph:
 35
 36        t -> f
 37        w -> e
 38        r -> t
 39        e -> r
 40
 41        The indegrees are:
 42
 43        r -> 1
 44        t -> 1
 45        f -> 1
 46        e -> 1
 47        w -> 0
 48
 49        In this case, we find any node with indegree 0 and do a topological sort.
 50        So in the above case, we visit w, add it to our output, and then check
 51        if its children, in this case, e are visitable.
 52
 53        e has an indegree of 1, so after w is visited, it is visitable, so
 54        we visit e.
 55
 56        Then, e allows us to visit r, which also has an indegree of 1, so after
 57        visiting e, r is visitable.
 58
 59        Then we visit r, which has a child of t, and we can visit t after visiting
 60        r because it has an indegree of 1.
 61
 62        Then we visit t, which allows us to visit f.
 63
 64        This allows us to construct the string "wertf", which is the only valid
 65        ordering for this example, but other examples may have more orderings.
 66
 67        We make sure that we've counted every char in the original set of words
 68        before returning the joined string.
 69
 70        """
 71        adj_list = defaultdict(set)
 72        indegrees = Counter({c: 0 for word in words for c in word})
 73
 74        for l, r in zip(words, words[1:]):
 75            for c, d in zip(l, r):
 76                if c != d:
 77                    if d not in adj_list[c]:
 78                        adj_list[c].add(d)
 79                        indegrees[d] += 1
 80                    break
 81            else:
 82                if len(r) < len(l):
 83                    return ""
 84
 85        output = []
 86        q = deque([c for c in indegrees if indegrees[c] == 0])
 87        while q:
 88            c = q.popleft()
 89            output.append(c)
 90            for d in adj_list[c]:
 91                indegrees[d] -= 1
 92                if indegrees[d] == 0:
 93                    q.append(d)
 94
 95        if len(output) < len(indegrees):
 96            return ""
 97        return "".join(output)
 98
 99
100# @leet end
101
102
103def test():
104    assert 2 + 2 == 4
class Solution:
 6class Solution:
 7    def alienOrder(self, words: list[str]) -> str:
 8        """
 9        This question asks us to return an ordering of characters given a set
10        of sorted words.
11
12        First, we describe the relation between the characters. We do this by
13        extracting the first character of each pair that doesn't match, and
14        noting that the left side char comes before the right side.
15
16        For these words:
17
18        wrt
19        wrf
20        er
21        ett
22        rftt
23
24        the pairs would be (wrt, wrf), (wrf, er), (er, ett), (ett, rftt).
25
26        For each pair, we want to find which ones come before, so:
27
28        (wrt, wrf) = t -> f (since wr are the same, t and f are the first differing
29        characters, and t is on the left side of our pair, so t comes before f).
30        (wrf, er) = w -> e (since w and e don't match, and w is on the left).
31        (er, ett) = r -> t (since e matches for both, and r is on the left).
32        (ett, rftt) = e -> r (since e must come before r).
33
34        As well, we want to create an indegree map that counts how man indegrees
35        each node has. For this graph:
36
37        t -> f
38        w -> e
39        r -> t
40        e -> r
41
42        The indegrees are:
43
44        r -> 1
45        t -> 1
46        f -> 1
47        e -> 1
48        w -> 0
49
50        In this case, we find any node with indegree 0 and do a topological sort.
51        So in the above case, we visit w, add it to our output, and then check
52        if its children, in this case, e are visitable.
53
54        e has an indegree of 1, so after w is visited, it is visitable, so
55        we visit e.
56
57        Then, e allows us to visit r, which also has an indegree of 1, so after
58        visiting e, r is visitable.
59
60        Then we visit r, which has a child of t, and we can visit t after visiting
61        r because it has an indegree of 1.
62
63        Then we visit t, which allows us to visit f.
64
65        This allows us to construct the string "wertf", which is the only valid
66        ordering for this example, but other examples may have more orderings.
67
68        We make sure that we've counted every char in the original set of words
69        before returning the joined string.
70
71        """
72        adj_list = defaultdict(set)
73        indegrees = Counter({c: 0 for word in words for c in word})
74
75        for l, r in zip(words, words[1:]):
76            for c, d in zip(l, r):
77                if c != d:
78                    if d not in adj_list[c]:
79                        adj_list[c].add(d)
80                        indegrees[d] += 1
81                    break
82            else:
83                if len(r) < len(l):
84                    return ""
85
86        output = []
87        q = deque([c for c in indegrees if indegrees[c] == 0])
88        while q:
89            c = q.popleft()
90            output.append(c)
91            for d in adj_list[c]:
92                indegrees[d] -= 1
93                if indegrees[d] == 0:
94                    q.append(d)
95
96        if len(output) < len(indegrees):
97            return ""
98        return "".join(output)
def alienOrder(self, words: list[str]) -> str:
 7    def alienOrder(self, words: list[str]) -> str:
 8        """
 9        This question asks us to return an ordering of characters given a set
10        of sorted words.
11
12        First, we describe the relation between the characters. We do this by
13        extracting the first character of each pair that doesn't match, and
14        noting that the left side char comes before the right side.
15
16        For these words:
17
18        wrt
19        wrf
20        er
21        ett
22        rftt
23
24        the pairs would be (wrt, wrf), (wrf, er), (er, ett), (ett, rftt).
25
26        For each pair, we want to find which ones come before, so:
27
28        (wrt, wrf) = t -> f (since wr are the same, t and f are the first differing
29        characters, and t is on the left side of our pair, so t comes before f).
30        (wrf, er) = w -> e (since w and e don't match, and w is on the left).
31        (er, ett) = r -> t (since e matches for both, and r is on the left).
32        (ett, rftt) = e -> r (since e must come before r).
33
34        As well, we want to create an indegree map that counts how man indegrees
35        each node has. For this graph:
36
37        t -> f
38        w -> e
39        r -> t
40        e -> r
41
42        The indegrees are:
43
44        r -> 1
45        t -> 1
46        f -> 1
47        e -> 1
48        w -> 0
49
50        In this case, we find any node with indegree 0 and do a topological sort.
51        So in the above case, we visit w, add it to our output, and then check
52        if its children, in this case, e are visitable.
53
54        e has an indegree of 1, so after w is visited, it is visitable, so
55        we visit e.
56
57        Then, e allows us to visit r, which also has an indegree of 1, so after
58        visiting e, r is visitable.
59
60        Then we visit r, which has a child of t, and we can visit t after visiting
61        r because it has an indegree of 1.
62
63        Then we visit t, which allows us to visit f.
64
65        This allows us to construct the string "wertf", which is the only valid
66        ordering for this example, but other examples may have more orderings.
67
68        We make sure that we've counted every char in the original set of words
69        before returning the joined string.
70
71        """
72        adj_list = defaultdict(set)
73        indegrees = Counter({c: 0 for word in words for c in word})
74
75        for l, r in zip(words, words[1:]):
76            for c, d in zip(l, r):
77                if c != d:
78                    if d not in adj_list[c]:
79                        adj_list[c].add(d)
80                        indegrees[d] += 1
81                    break
82            else:
83                if len(r) < len(l):
84                    return ""
85
86        output = []
87        q = deque([c for c in indegrees if indegrees[c] == 0])
88        while q:
89            c = q.popleft()
90            output.append(c)
91            for d in adj_list[c]:
92                indegrees[d] -= 1
93                if indegrees[d] == 0:
94                    q.append(d)
95
96        if len(output) < len(indegrees):
97            return ""
98        return "".join(output)

This question asks us to return an ordering of characters given a set of sorted words.

First, we describe the relation between the characters. We do this by extracting the first character of each pair that doesn't match, and noting that the left side char comes before the right side.

For these words:

wrt wrf er ett rftt

the pairs would be (wrt, wrf), (wrf, er), (er, ett), (ett, rftt).

For each pair, we want to find which ones come before, so:

(wrt, wrf) = t -> f (since wr are the same, t and f are the first differing characters, and t is on the left side of our pair, so t comes before f). (wrf, er) = w -> e (since w and e don't match, and w is on the left). (er, ett) = r -> t (since e matches for both, and r is on the left). (ett, rftt) = e -> r (since e must come before r).

As well, we want to create an indegree map that counts how man indegrees each node has. For this graph:

t -> f w -> e r -> t e -> r

The indegrees are:

r -> 1 t -> 1 f -> 1 e -> 1 w -> 0

In this case, we find any node with indegree 0 and do a topological sort. So in the above case, we visit w, add it to our output, and then check if its children, in this case, e are visitable.

e has an indegree of 1, so after w is visited, it is visitable, so we visit e.

Then, e allows us to visit r, which also has an indegree of 1, so after visiting e, r is visitable.

Then we visit r, which has a child of t, and we can visit t after visiting r because it has an indegree of 1.

Then we visit t, which allows us to visit f.

This allows us to construct the string "wertf", which is the only valid ordering for this example, but other examples may have more orderings.

We make sure that we've counted every char in the original set of words before returning the joined string.

def test():
104def test():
105    assert 2 + 2 == 4