2019 카카오 신입 공채 1차 코딩 테스트 Swift 풀이

# 문제 해설

http://tech.kakao.com/2018/09/21/kakao-blind-recruitment-for2019-round-1/

# 오픈채팅방

https://www.welcomekakao.com/learn/courses/30/lessons/42888

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import Foundation

func solution(_ record:[String]) -> [String] {
    
    var answer = [String]()
    
    var userList = [String : String]()
    var chatLog = [[String]]()
    
    let enterMsg = "님이 들어왔습니다."
    let leaveMsg = "님이 나갔습니다."
    
    // chatLog 작성
    for info in record {
        let splitInfo = info.components(separatedBy: " ")
        if splitInfo[0] == "Enter" {
            userList[splitInfo[1]] = splitInfo[2]
            chatLog.append([splitInfo[1], enterMsg])
        } else if splitInfo[0] == "Leave" {
            chatLog.append([splitInfo[1], leaveMsg])
        } else if splitInfo[0] == "Change" {
            userList[splitInfo[1]] = splitInfo[2]
        }
    }
    
    // 최종 answer 작성
    for log in chatLog {
        answer.append("\(userList[log[0]]!)\(log[1])")
    }

    return answer
}

# 실패율

https://www.welcomekakao.com/learn/courses/30/lessons/42889

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import Foundation

func solution(_ N:Int, _ stages:[Int]) -> [Int] {
    // 실패수가 0인 셋은 계산하지 않기 위해 스테이지에 머물러있는 사람의 수를 계수
    var table = [Int : Int]()
    for stage in stages {
        if let counter = table[stage] {
            // 값이 있으면 기존 항목의 value를 1 증가시킴
            table[stage] = counter + 1
        } else {
            // 값이 없으면 새 항목을 생성
            table[stage] = 1
        }
    }
    
    var failureRate = [Int : Double]()
    // 1 스테이지는 모든 사람이 참가하므로 초기값은 stages.count
    var reached = stages.count
    for i in 1...N {
        if let notClear = table[i] {
            let rate = Double(notClear) / Double(reached)
            failureRate[i] = rate

            // 통과하지 못한 사람만큼 다음스테이지의 총인원수에서 빠져나감
            reached -= notClear
        } else {
            // 게임중인 플레이어가 없는 스테이지는 0으로 고정
            failureRate[i] = 0.0
        }
    }
    
    // 키값으로 정렬한 뒤에 밸류값으로 정렬
    let sortedErr = failureRate.sorted(by: <).sorted(by: { $0.value > $1.value })
    return sortedErr.map { $0.key }
}

# 후보키

https://www.welcomekakao.com/learn/courses/30/lessons/42890

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
import Foundation

func solution(_ relation:[[String]]) -> Int {
    
    var candidateKey = [Int]()
    let row = relation.count
    let col = relation[0].count
    
    // 2^col 갯수만큼 키 조합을 만들 수 있음
    for i in 1 ..< Int(pow(2.0, Double(col))) {
        // 중복을 없애주는 tempSet으로 조합가능한 키 묶음을 생성
        var tempSet = Set<String>()
        for j in 0..<row {
            var tmp = ""
            for k in 0..<col {
                if (i & (1 << k)) != 0 {
                    tmp += relation[j][k]
                }
            }
            tempSet.insert(tmp)
        }
        
        // 중복이 없는 키 묶음에 대해서만 연산하여 유일성 획득
        if tempSet.count == row {
            var contains = true
            
            // 후보키의 최소성 판정
            for num in candidateKey {
                if (num & i) == num {
                    contains = false
                    break
                }
            }
            // 후보키가 없으면 추가
            if contains {
                candidateKey.append(i)
            }
        }
    }
    
    return candidateKey.count
}

# 무지의 먹방 라이브

https://www.welcomekakao.com/learn/courses/30/lessons/42891

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import Foundation

func solution(_ food_times:[Int], _ K:Int64) -> Int {
    // 모든 음식을 방송이 중단되기 전에 다 먹을 수 있는 경우
    guard food_times.reduce(0, +) > K else {
        return -1
    }
    // 64비트 머신에서 Int와 Int64는 동일한 값이다
    var k: Int = Int(K)

    // 시간과 인덱스를 저장한 배열을 작성
    var food_info: [(time: Int, idx: Int)] = food_times.enumerated().map { ($1,$0) }
        .sorted(by: <)

    var i = 0, j = 0, cycle = 0
    
    // 일괄적으로 먹어치울 수 있는 부분을 처리
    while i < food_info.count {
        j = i
        
        // 먹는 시간이 같은 음식의 총 갯수를 j로 확인
        while j < food_info.count
            && food_info[i].time == food_info[j].time { j += 1 }
        
        let eats = food_info[i].time - cycle
        let dec = (food_info.count - i) * eats
        // 다음 음식을 먹는데 필요한 총 시간보다 남은 시간이 짧으면 루프 탈출
        if dec > k { break }
        
        // 먹는데 소요된 시간을 k에서 빼 준다
        k -= dec
        cycle += eats
        // 다음 탐색할 인덱스를 다 먹은 음식 다음걸로 옮김
        i = j
    }
    
    // 루프를 돌리고도 못 먹은 음식들의 인덱스를 오름차순 정렬
    food_info = food_info[i...].sorted { $0.idx < $1.idx }
    // 남은 시간을 루프로 돌릴 수 있을 만큼 전부 소모하고 난 후의 값이 음식 인덱스
    k = k % food_info.count
    
    // 배열 인덱스에 +1 한 값이 방송 재개후 먹을 음식이 됨
    return food_info[k].idx + 1
}

# 길 찾기 게임

https://www.welcomekakao.com/learn/courses/30/lessons/42892

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
import Foundation

class Node {
    var key: Int = 0 // x좌표
    var value: Int = 0 // 값
    var l_child: Node?
    var r_child: Node?
    
    init(key: Int, value: Int) {
        self.key = key
        self.value = value
    }
}

class Tree {
    var root: Node?

    func put(node: Node) {
        if self.root == nil {
            // 루트가 널이면 값을 바로 저장
            self.root = node
        } else {
            var c_node = self.root
            var is_put = false
            
            while !is_put {
                if node.key < c_node!.key {
                    if c_node!.l_child == nil {
                        c_node!.l_child = node
                        is_put = true
                    } else {
                        c_node = c_node!.l_child
                    }
                } else {
                    if c_node!.r_child == nil {
                        c_node!.r_child = node
                        is_put = true
                    } else {
                        c_node = c_node!.r_child
                    }
                }
            }
        }
    }

    // 전위 순회
    func preorder() -> [Int] {
        
        var stack = [self.root]
        var visit = [Int]()

        while !stack.isEmpty {
            // 맨 뒤의 노드값을 반환 후 삭제
            let node = stack.popLast()!
            visit.append(node!.value)
            
            // 왼쪽 노드가 맨 뒤로 오도록 한다
            if node?.r_child != nil {
                stack.append(node?.r_child)
            }
            if node?.l_child != nil {
                stack.append(node?.l_child)
            }
        }
        
        return visit
    }

    // 후위 순회
    func postorder() -> [Int] {

        var stack = [self.root]
        var visit = [Int]()
        
        while !stack.isEmpty {
            let node = stack.popLast()!
            visit.append(node!.value)

            // 오른쪽 노드가 맨 뒤로 오도록 한다
            if node?.l_child != nil {
                stack.append(node?.l_child)
            }
            if node?.r_child != nil {
                stack.append(node?.r_child)
            }
        }
        
        return visit.reversed()
    }
}


func solution(_ nodeinfo:[[Int]]) -> [[Int]] {
    
    var answer = [[Int]]()
    // [높이 : [(값, [X좌표, Y좌표])]]
    var depth_dic = [Int : [(Int, [Int])]]()
    
    for (i, node) in nodeinfo.enumerated() {
        if depth_dic[node[1]] != nil {
            // 같은 높이를 가진 다른 값이 이미 등록되어 있을 경우 추가
            depth_dic[node[1]]?.append((i + 1, node))
        } else {
            // 값이 없을경우 딕셔너리 쌍을 새로 작성
            depth_dic[node[1]] = [(i + 1, node)]
        }
    }
    
    let tree = Tree()
    
    // 높이가 높은 순서부터 트리에 집어넣는다
    let sorted_keys = depth_dic.keys.sorted(by: >)
    for level in sorted_keys {
        if let x = depth_dic[level] {
            // 왼쪽부터 순서대로 트리에 넣기 위해 x좌표값이 작은 순서부터 정렬
            let sibling = x.sorted(by: { $0.1[0] < $1.1[0] })
            // 바이너리 트리 작성
            for node in sibling {
                tree.put(node: Node(key: node.1[0], value: node.0))
            }
        }
    }
    
    answer.append(tree.preorder())
    answer.append(tree.postorder())
    
    return answer
}

# 매칭 점수

https://www.welcomekakao.com/learn/courses/30/lessons/42893

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
import Foundation

extension String{
    func getAllMatchAfterRegex(_ pattern: String, _ option: NSRegularExpression.Options) -> [String] {
        // 옵셔널을 벗기기 위해 do-catch를 사용
        do {
            let regex = try NSRegularExpression(pattern: pattern, options: option)
            let range = NSRange(self.startIndex..., in: self)
            let matches = regex.matches(in: self, range: range)
            return matches.map {
                String(self[Range($0.range, in: self)!])
            }
        } catch let error {
            print("invalid regex: \(error.localizedDescription)")
            return []
        }
    }
}

class Page {
    var url = "" // url
    var normal = 0 // 기본 점수
    var external_links = [String]() // 외부링크 list
    var matching = 0.0 // 매칭 점수
    
    init(_ url: String, _ normal: Int, _ external_links: [String]) {
        self.url = url
        self.normal = normal
        self.external_links = external_links
        self.matching = Double(normal)
    }
}

func parse_html(word: String, html: String) -> (String, Int, [String]) {
    
    // target과 일치하는 단어를 리스트에서 계수
    func count(target: String, word_list: [String]) -> Int {
        var result = 0
        for word in word_list {
            if word.lowercased() == target.lowercased() {
                result += 1
            }
        }
        
        return result
    }
    
    // head -> meta 태그안의 url을 추출
    let head = html.getAllMatchAfterRegex("<head>(.+)</head>", .dotMatchesLineSeparators)
    let url = head[0].getAllMatchAfterRegex("(?<=meta property=\"og:url\" content=\"https://)([^\"]+)", .dotMatchesLineSeparators)
    
    // 기본점수 계산
    let word_list = html.getAllMatchAfterRegex("[a-zA-Z]+", [])
    let normal = count(target: word, word_list: word_list)
    
    // 후방탐색으로 외부링크 추출
    let ext_links = html.getAllMatchAfterRegex("(?<=<a href=\"https://)([^\"]+)", .dotMatchesLineSeparators)
    let external_links = Array(Set(ext_links)) // 중복 삭제
    
    return (url[0], normal, external_links)
}


func solution(_ word:String, _ pages:[String]) -> Int {
    
    var dic = [String : (Int, Page)]() // key: url, value: (idx, Page)
    
    // pages를 순회하면서 Page 객체를 생성
    for (idx, html) in pages.enumerated() {
        let (url, normal, external_links) = parse_html(word: word, html: html)
        let page = Page(url, normal, external_links)
        dic[url] = (idx, page)
    }
    
    // 모든 Page 객체에 대해 매칭점수를 계산
    for url in dic {
        let page = url.value.1
        let external_links = page.external_links
        if external_links.count != 0 {
            let power = Double(page.normal) / Double(external_links.count)
            for link in external_links {
                if dic[link] != nil {
                    dic[link]!.1.matching += power
                }
            }
        }
    }
    
    // 정렬을 위해 dic을 list로 변환
    var answer = [(Int, Page)]()
    for url in dic {
        answer.append(url.value)
    }
    
    // idx에 대해 오름차순으로 정렬 후 rank에 대해 내림차순으로 정렬
    answer = answer.sorted(by: { $0.0 < $1.0 }).sorted(by: { $0.1.matching > $1.1.matching})
    
//     arr 내용확인
//    for tup in answer {
//        print((tup.0, tup.1.url, tup.1.normal, tup.1.external_links, tup.1.matching))
//    }
    
    return answer[0].0
}

# 블록 게임

https://www.welcomekakao.com/learn/courses/30/lessons/42894

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
import Foundation

var n = 0
// 2x3 블록을 확인하기 위한 x, y 인덱스 모음
let block2x3 = [[0, 0, 0, 1, 1, 1], [0, 0, 1, 1, 2, 2]]
// 3x2 블록을 확인하기 위한 x, y 인덱스 모음
let block3x2 = [[0, 1, 2, 0, 1, 2], [0, 1, 0, 1, 0, 1]]

// x, y값이 인덱스에서 벗어나는지를 판정
func isXYsafe(_ x: Int, _ y: Int, _ n: Int) -> Bool {
    return min(x, y) >= 0 && max(x, y) < n
}


func possible(_ x: Int, _ y: Int, _ k: Int, _ count: [[Int]], _ matrix: [[Int]], _ check: [[Int]], _ n: Int) -> ([[Int]], [[Int]], [[Int]], Bool) {
    
    var col = -1
    var cur = 0
    var check = check
    
    for i in 0 ..< 6 {
        let nx = x + block2x3[k][i]
        let ny = y + block3x2[k][i]
        // 같은 숫자가 4개 있고 0이 두개인 6개짜리 블록을 찾는다
        if isXYsafe(nx, ny, n) {
            if matrix[nx][ny] != 0 {
                if col == -1 {
                    col = matrix[nx][ny]
                } else {
                    if col != matrix[nx][ny] {
                        return (count, matrix, check, false)
                    }
                }
            } else {
                cur += 1
                // 보드에서 0인 곳이 count에서 0이 아니라면 블록 아래쪽이라는 말이므로 연산 종료
                if count[nx][ny] != 0 {
                    return (count, matrix, check, false)
                }
            }
        } else {
            return (count, matrix, check, false)
        }
    }
    
    if cur != 2 {
        return (count, matrix, check, false)
    }
    
    // 블록을 지울 수 있었던 경우
    for i in 0 ..< 6 {
        let nx = x + block2x3[k][i]
        let ny = y + block3x2[k][i]
        check[nx][ny] = 1 // true 대신 1로 함
    }
    
    // while 연산을 한번 더 하도록 함
    return (count, matrix, check, true)
}


func solution(_ board:[[Int]]) -> Int {
    
    n = board.count
    var answer = 0
    var possi = Bool()

    // 입력받는 행렬, 블록 체크 후 지워지는것도 반영
    var board = board
    //  검은 블록을 내릴 수 있는지 판정하는 보드
    var count = Array(repeating: Array(repeating: 0, count: n), count: n)
    // 지워야 할 블록을 알려주는 행렬
    var check = Array(repeating: Array(repeating: 0, count: n), count: n)
    
    while true {
        // count, check보드 초기확
        for i in 0 ..< n {
            for j in 0 ..< n {
                count[i][j] = 0
                check[i][j] = 0
            }
        }
        var flag = false
        
        // board 정보로부터 count 보드를 작성. 블록이 있는지만 우선 판정
        for i in 0 ..< n {
            for j in 0 ..< n {
                if board[i][j] > 0 {
                    count[i][j] = 1
                } else {
                    count[i][j] = 0
                }
            }
        }
        // 블록 값을 밑으로 내리면서 계속 더한다. 그럼 검은 블록은 블록 밑에 올 수 없음
        for i in 1 ..< n {
            for j in 0 ..< n {
                count[i][j] += count[i-1][j]
            }
        }
        
        for i in 0 ..< n {
            for j in 0 ..< n {
                for k in 0 ..< 2 {
                    (count, board, check, possi) = possible(i, j, k, count, board, check, n)
                    
                    if possi {
                        // 연산이 끝나지 않았으므로 루프를 다시 실행시킴
                        flag = true
                        answer += 1
                    }
                }
            }
        }
        
        // 지워질 수 있는 행렬을 check보드로 판정 후 board에서 삭제
        for i in 0 ..< n {
            for j in 0 ..< n {
                if check[i][j] == 1 {
                    board[i][j] = 0
                }
            }
        }
        
        if !flag {
            break
        }
    }
    
    return answer
}
Built with Hugo
Theme Stack designed by Jimmy