Skip to content

Commit

Permalink
add solution: alien dictionary
Browse files Browse the repository at this point in the history
  • Loading branch information
obzva committed Nov 20, 2024
1 parent 8112a9c commit cf06fdb
Showing 1 changed file with 105 additions and 0 deletions.
105 changes: 105 additions & 0 deletions alien-dictionary/flynn.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/*
ํ’€์ด
- ๋‘ ๋‹จ์–ด๋ฅผ ์•ŒํŒŒ๋ฒณ ํ•˜๋‚˜์”ฉ ์ฐจ๋ก€๋Œ€๋กœ ๋น„๊ตํ–ˆ์„ ๋•Œ, ์ฒซ๋ฒˆ์งธ ์ฐจ์ด๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ์ง€์ ์—์„œ alien dictionary์˜ order๋ฅผ ์ฐพ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค
- ์ฒซ๋ฒˆ์งธ ๋‹จ์–ด๋ถ€ํ„ฐ ๋ฐ”๋กœ ๊ทธ ๋‹ค์Œ ๋‹จ์–ด์™€ ๋‘ ๋‹จ์–ด์”ฉ ์ง์ง€์–ด์„œ ๋น„๊ตํ•˜๋ฉด ์•ž์—์„œ ๋งํ•œ ์ผ๋ จ์˜ order๋ฅผ ์ฐพ์•„๋‚ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค
์•ŒํŒŒ๋ฒณ x๊ฐ€ ์•ŒํŒŒ๋ฒณ y๋ณด๋‹ค alien dictionary order์—์„œ ์•ž์„œ๋Š” ๊ด€๊ณ„, ์ฆ‰ x->y์ธ ๊ด€๊ณ„๋ฅผ ์ฐพ์•„์„œ x: {y1, y2, y3, ...}์ธ ์ง‘ํ•ฉ์˜ map์„ ๋งŒ๋“ค๊ฒ ์Šต๋‹ˆ๋‹ค
๊ทธ๋ฆฌ๊ณ  ์ด๋ฅผ nextLetters๋ผ๊ณ  ๋ช…๋ช…ํ•˜์˜€์Šต๋‹ˆ๋‹ค
- ๋งŒ์•ฝ ํŠน์ • ์•ŒํŒŒ๋ฒณ x์— ๋Œ€ํ•ด, z->x์ธ ์•ŒํŒŒ๋ฒณ z๊ฐ€ ์—†๋‹ค๋ฉด x๋Š” ์šฐ๋ฆฌ๊ฐ€ ์ •๋‹ต์œผ๋กœ ์ œ์ถœํ•  result string์˜ ์–ด๋Š ์œ„์น˜์—๋“  ์ž์œ ๋กญ๊ฒŒ ๋ผ์›Œ๋„ฃ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค
(* If there are multiple solutions, return any of them.)
์šฐ๋ฆฌ๋Š” ์ด๋Ÿฐ ์•ŒํŒŒ๋ฒณ x๋ฅผ ์ฐพ์•„๋‚ผ ๋•Œ๋งˆ๋‹ค ๋ฐ”๋กœ๋ฐ”๋กœ result string res์— ์ถ”๊ฐ€ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค
- z->x์ธ ์•ŒํŒŒ๋ฒณ z๊ฐ€ ํ˜„์žฌ ์žˆ๋Š”์ง€ ์—†๋Š”์ง€์— ๋Œ€ํ•œ ์ƒํƒœ๊ด€๋ฆฌ๋ฅผ ํ•˜๊ธฐ ์œ„ํ•ด์„œ prevLetterCounts๋ผ๋Š” map์„ ๋งŒ๋“ค๊ฒ ์Šต๋‹ˆ๋‹ค
prevLetterCounts[x]: z->x์ธ z์˜ ๊ฐœ์ˆ˜
- nextLetters, prevLetterCounts๋ฅผ ์ž˜ ์ƒ์„ฑํ•œ ํ›„์—๋Š” prevLetterCount๊ฐ€ 0์ธ ์•ŒํŒŒ๋ฒณ๋ถ€ํ„ฐ queue์— ๋“ฑ๋ก์‹œํ‚จ ํ›„ BFS๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค
BFS๋ฅผ ์‹คํ–‰ํ•˜๋ฉฐ prevLetterCount๊ฐ€ 0์ธ ์•ŒํŒŒ๋ฒณ์ด ์ƒˆ๋กœ ๋ฐœ๊ฒฌ๋  ๊ฒฝ์šฐ queue์— ๋“ฑ๋ก์‹œํ‚ต๋‹ˆ๋‹ค
- ์—ฃ์ง€์ผ€์ด์Šค๊ฐ€ ๋‘ ๊ฒฝ์šฐ ๋ฐœ์ƒํ•˜๋Š”๋ฐ,
์ฒซ๋ฒˆ์งธ๋Š” nextLetters๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๋ฐ˜๋ณต๋ฌธ์—์„œ ๋ฐœ๊ฒฌ๋ฉ๋‹ˆ๋‹ค
๋‘๋ฒˆ์งธ ๋‹จ์–ด๊ฐ€ ์ฒซ๋ฒˆ์งธ ๋‹จ์–ด์˜ prefix์ธ ๊ฒฝ์šฐ๋Š” ์• ์ดˆ๋ถ€ํ„ฐ ์ž˜๋ชป๋œ dictionary order์ธ ๊ฒฝ์šฐ์ž…๋‹ˆ๋‹ค
์œ„ ๊ฒฝ์šฐ๋Š” ๋‹จ์ˆœ ์•ŒํŒŒ๋ฒณ ๋น„๊ต๋กœ๋Š” ๋ฐœ๊ฒฌํ•˜๊ธฐ ์–ด๋ ค์šฐ๋ฏ€๋กœ flag๋ฅผ ์‚ฌ์šฉํ•˜์˜€์Šต๋‹ˆ๋‹ค
๋‘๋ฒˆ์งธ๋Š” result string์˜ ๊ธธ์ด๊ฐ€ input์œผ๋กœ ์ฃผ์–ด์ง„ ๋‹จ์–ด๋“ค์— ๋“ฑ์žฅํ•œ ์•ŒํŒŒ๋ฒณ์˜ ๊ฐœ์ˆ˜๋ณด๋‹ค ์ ์€ ๊ฒฝ์šฐ์ž…๋‹ˆ๋‹ค
์ด ๊ฒฝ์šฐ๋Š” nextLetters์— ์ˆœํ™˜์ด ๋ฐœ์ƒํ•œ ๊ฒฝ์šฐ์ด๋ฏ€๋กœ dictionary order๊ฐ€ ์ž˜๋ชป๋˜์—ˆ๋‹ค๊ณ  ํŒ๋‹จํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค
Big O
- N: ์ฃผ์–ด์ง„ ๋ฐฐ์—ด words์˜ ๊ธธ์ด
- S(W): ๋ฐฐ์—ด words์— ์†ํ•œ ๋ชจ๋“  string์˜ ๊ธธ์ด์˜ ์ดํ•ฉ
- Time complexity: O(N + S(W))
- prevLetterCounts์™€ nextLetters ์ƒ์„ฑ -> O(N)
- nextLetters์— ๋“ค์–ด๊ฐˆ ์•ŒํŒŒ๋ฒณ ์ „ํ›„๊ด€๊ณ„ ์ฐพ๊ธฐ -> O(S(W))
- ์•ŒํŒŒ๋ฒณ ์†Œ๋ฌธ์ž์˜ ์ˆ˜๋Š” ์ œํ•œ๋˜์–ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์— BFS์˜ ์‹œ๊ฐ„ ๋ณต์žก๋„ ์ƒํ•œ์„ ์€ ์ •ํ•ด์ ธ ์žˆ์Šต๋‹ˆ๋‹ค -> O(26 * 26) = O(1)
- Space complexity: O(1)
- ์•ŒํŒŒ๋ฒณ ์†Œ๋ฌธ์ž์˜ ์ˆ˜๋Š” ์ œํ•œ๋˜์–ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๊ณต๊ฐ„ ๋ณต์žก๋„์˜ ์ƒํ•œ์„ ์€ ์ •ํ•ด์ ธ ์žˆ์Šต๋‹ˆ๋‹ค
prevLetterCounts -> O(26) = O(1)
nextLetters -> O(26 * 26) = O(1)
queue -> O(26) = O(1)
*/

import "strings"

func alienOrder(words []string) string {
n := len(words)
// prevLetterCounts[x] = count of letters y that are in relation of y->x
prevLetterCounts := make(map[string]int)
// nextLetters[x] = set of letters y that are in relation of x->y
nextLetters := make(map[string]map[string]bool)
for _, word := range words {
for _, c := range word {
if _, ok := prevLetterCounts[string(c)]; !ok {
prevLetterCounts[string(c)] = 0
nextLetters[string(c)] = make(map[string]bool)
}
}
}

for i := 0; i < n-1; i++ {
currWord := words[i]
nextWord := words[i+1]
// flag for edge case below
diff := false
for j := 0; j < len(currWord) && j < len(nextWord); j++ {
if currWord[j] != nextWord[j] {
diff = true
if _, ok := nextLetters[string(currWord[j])][string(nextWord[j])]; !ok {
prevLetterCounts[string(nextWord[j])]++
nextLetters[string(currWord[j])][string(nextWord[j])] = true
}
break
}
}
// tricky edge case!!!
// if nextWord is prefix of currWord, then the provided dictionary order is invalid
if !diff && len(currWord) > len(nextWord) {
return ""
}
}
// BFS
queue := make([]string, 0, len(prevLetterCounts))
for letter := range prevLetterCounts {
// we can arrange letters whose prevLetterCount is zero as we wish
if prevLetterCounts[letter] == 0 {
queue = append(queue, letter)
}
}
// in Go, using strings.Builder is the most efficient way to build strings
var sb strings.Builder
for len(queue) > 0 {
// pop the letter from the queue and append it to the result string
popped := queue[0]
queue = queue[1:]
sb.WriteString(popped)

for nextLetter := range nextLetters[popped] {
prevLetterCounts[nextLetter]--
// if prevLetterCount for nextLetter becomes zero, we can add it to the queue
// append to the result string (order) in the next iteration
if prevLetterCounts[nextLetter] == 0 {
queue = append(queue, nextLetter)
}
}
}
// res is result string
res := sb.String()
// this case means that there was a cycle
if len(res) != len(prevLetterCounts) {
return ""
}
// else return res
return res
}

0 comments on commit cf06fdb

Please sign in to comment.