Alien Dictionary
HardThe problem
words is a list of words from an alien language, sorted in that language's alphabet order. Work out the order of the letters and return them as a string, using only the letters that appear in words. If the list contradicts itself (no valid letter order exists), return an empty string.
- Example 1Input: words = ["wrt", "wrf", "er", "ett", "rftt"]Output: "wertf"
Comparing neighbouring words: t comes before f, w before e, r before t, e before r. That forces w, e, r, t, f.
- Example 2Input: words = ["z", "x", "z"]Output: ""
The first two words say z is before x, and the last two say x is before z.
- Example 3Input: words = ["abc", "ab"]Output: ""
A longer word cannot come before its own shorter prefix.
- 1 ≤ len(words) ≤ 100
- 1 ≤ len(words[i]) ≤ 100
- Words use only lowercase English letters
- When the order is not forced, any valid order may be accepted
Write it in Go. Try for about 20 minutes on paper first, then open one hint at a time.
Try it here
Write Go. Common packages like fmt and sort are imported for you. Keep the function name and inputs the same.
Hints, one at a time
Nudge
Compare adjacent words: the first different letter tells you one ordering rule between two letters.
The idea
Build edges from adjacent word pairs (watch for the invalid "abc" before "ab" case), then topologically sort; a cycle means no valid order.
Target: O(total characters) time
Go function shape
func foreignDictionary(words []string) stringReference solution
Tested with go test. Try it yourself first, then compare.
// AlienOrder: letter order implied by a sorted list of words, or "" if it is contradictory.
func AlienOrder(words []string) string {
next := map[byte]map[byte]bool{}
indeg := map[byte]int{}
for _, w := range words {
for i := 0; i < len(w); i++ {
if _, ok := indeg[w[i]]; !ok {
indeg[w[i]] = 0
next[w[i]] = map[byte]bool{}
}
}
}
for i := 0; i+1 < len(words); i++ {
a, b := words[i], words[i+1]
if len(a) > len(b) && a[:len(b)] == b {
return "" // "abc" can never come before "ab"
}
for j := 0; j < len(a) && j < len(b); j++ {
if a[j] != b[j] { // the first difference is the only rule this pair gives us
if !next[a[j]][b[j]] {
next[a[j]][b[j]] = true
indeg[b[j]]++
}
break
}
}
}
var queue []byte
for c := 'a'; c <= 'z'; c++ { // fixed scan order keeps the output deterministic
if d, ok := indeg[byte(c)]; ok && d == 0 {
queue = append(queue, byte(c))
}
}
var out []byte
for len(queue) > 0 {
c := queue[0]
queue = queue[1:]
out = append(out, c)
var outs []byte
for to := range next[c] {
outs = append(outs, to)
}
sort.Slice(outs, func(i, j int) bool { return outs[i] < outs[j] })
for _, to := range outs {
if indeg[to]--; indeg[to] == 0 {
queue = append(queue, to)
}
}
}
if len(out) != len(indeg) {
return "" // a cycle: some letters never reached in-degree 0
}
return string(out)
}