2018 카카오 신입 공채 1차 코딩 테스트 Swift 답안

# 문제해설

http://tech.kakao.com/2017/09/27/kakao-blind-recruitment-round-1/

# 비밀지도 (비트연산)

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
func solution(_ n:Int, _ arr1:[Int], _ arr2:[Int]) -> [String] {
    var answer = [String](repeating: "", count: n)
    var bitAdded = Array<Int>()
    
    for i in 0..<n {
        bitAdded.append(arr1[i] | arr2[i])
    }
    
    for i in 0..<n {
        for _ in 0..<n {
            // 2진수로 변환한 값의 맨 뒷자리가 튀어나온다
            // 그걸 반환배열의 맨 앞으로 넣어준다
            answer[i].insert(bitAdded[i] % 2 == 1 ? "#" : " ", at: answer[i].startIndex)
            // 비트를 오른쪽으로 하나 옮김
            bitAdded[i] >>= 1
        }
    }
    return answer
}

# 다트 게임 (정규표현식)

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

 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
import Foundation

// 정규표현식을 쓸수있도록 스트링 타입을 확장
extension String{
    func getAllMatchAfterRegex(_ pattern: String) -> [String] {
        // 옵셔널을 벗기기 위해 do-catch를 사용
        do {
            let regex = try NSRegularExpression(pattern: pattern, options: [])
            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 []
        }
    }
}

func solution(_ dartResult: String) -> Int {
    
    // 입력을 정규표현식으로 자르기
    let pointArr = dartResult.getAllMatchAfterRegex("[0-9]+")  // 두자리수까지 탐색
    let bonusArr = dartResult.getAllMatchAfterRegex("[(S|D|T)]")  // 고정인 알파벳만 탐색
    let optionArr = dartResult.getAllMatchAfterRegex("(?<=S|D|T)[(*|#)]?")  // 후방탐색
    
    var result = [Int]()
    for i in 0..<3 {
        let newArray = [pointArr[i], bonusArr[i], optionArr[i]]
        
        // 점수 결정
        let point = Double(newArray[0])!
        
        // 보너스값 결정
        var bonus: Double = 1
        if newArray[1] == "D" {
            bonus = 2
        } else if newArray[1] == "T" {
            bonus = 3
        }
        
        // 옵션 결정 후 값 반영
        if newArray[2] == "*" {
            if i == 0 {
                result.append(Int(pow(point, bonus) * 2))
            } else {
                // 바로 앞의 결과값에 2를 곱해줌
                result[result.index(before: result.endIndex)] *= 2
                result.append(Int(pow(point, bonus) * 2))
            }
        } else if newArray[2] == "#" {
            result.append(Int(pow(point, bonus) * -1))
        } else {
            result.append(Int(pow(point, bonus)))
        }
    }
    
    return result.reduce(0) { $0 + $1 }
}

# 캐시

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

 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(_ cacheSize:Int, _ cities:[String]) -> Int {
    let CACHE_HIT = 1
    let CACHE_MISS = 5
    
    var answer = 0
    var cache = [String : Int]()
    var time = 0
    
    if cacheSize == 0 {
        answer += cities.count * CACHE_MISS
        return answer
    }
    
    for city in cities {
        time += 1
        
        // 대소문자 구분 없앰
        let city = city.lowercased()
        
        if cache[city] != nil {
            // 캐시가 히트하면 인덱스의 타임을 최신값으로 갱신
            cache[city] = time
            answer += CACHE_HIT
        } else {
            // 캐시미스일때 현재 캐시크기가 캐시사이즈보다 작으면 그냥 추가
            if cache.count < cacheSize {
                cache[city] = time
                answer += CACHE_MISS
            } else {
                // 그렇지 않으면 가장 오래된 캐시를 삭제하고 값을 추가
                let oldest = cache.min { a, b in a.value < b.value }!
                cache.remove(at: cache.index(forKey: oldest.key)!)
                cache[city] = time
                answer += CACHE_MISS
            }
        }
    }
    
    return answer
}

# 셔틀버스 (문자열 시간변환, 딕셔너리)

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

 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
import Foundation

func solution(_ n:Int, _ t:Int, _ m:Int, _ timetable:[String]) -> String {

    // 문자열 타임테이블을 가공하기 용이하게 숫자로 변환
    var cvtTimetable = [Int]()
    // 시간을 네자리 정수로 변환
    for time in timetable {
        let convert = time[time.startIndex].wholeNumberValue! * 1000
                + time[time.index(time.startIndex, offsetBy: 1)].wholeNumberValue! * 100
                + time[time.index(time.startIndex, offsetBy: 3)].wholeNumberValue! * 10
                + time[time.index(time.startIndex, offsetBy: 4)].wholeNumberValue!
        
        cvtTimetable.append(convert)
    }
    
    // 오름차순으로 소트
    cvtTimetable = cvtTimetable.sorted(by: <)
    
    // 버스 출발타임을 0900으로 설정
    var busStartTime: Int = 0900
    var answer: Int = 0
    var crewIdx: Int = 0
    
    // 각 버스에 대한 체크 시작
    for _ in 0..<n {
        var crewMember: Int = 0
        var arriveTime: Int = 0
        // 크루도착시간과 도착순서 관계 컨테이너 초기화
        var watingCue = [Int : Int]()
        
        // 버스 출발시간보다 빨리 온 크루의 도착시간 딕셔너리를 작성
        while (crewMember < m && crewIdx < timetable.count) {
            // 크루가 버스보다 늦게 왔으면 추가하지 않고 지나감
            if (cvtTimetable[crewIdx] > busStartTime) {
                break
            }
            // 버스 출발시간보다 빨리 온 크루를 딕셔너리에 순서대로 등록
            watingCue[cvtTimetable[crewIdx]] = crewIdx + 1
            crewIdx += 1
            crewMember += 1
        }
        
        // 타려고 대기한 크루의 배열을 소팅함
        let sortedCue = watingCue.sorted(by: <)
        
        
        // 타려고 대기한 총 인원이 버스의 승차가능인보다 적으면 버스출발시간에 오면 됨
        if (crewMember < m) {
            answer = max(answer, busStartTime)
        } else {
            // 대기멤버가 승차가능인보다 많으면 연산시작
            var check = 0
            for cue in sortedCue {
                // 탑승가능한 맨 마지막 크루에 대해 체크
                if (check + cue.value >= m) {
                    // 그 크루보다 1분 빠르게 도착하도록 함
                    arriveTime = cue.key - 1
                    
                    // 08:99를 08:59로 바꿔줌
                    if (arriveTime % 100 > 59) {
                        arriveTime -= 40
                    }
                    // 00:00 에서 -1 하면 24:00이 되도록 보정
                    if arriveTime < 0 {
                        arriveTime += 2400
                    }
                }
                check += cue.value
            }
            answer = max(answer, arriveTime)
        }
        
        // 시작시간을 버스 다음 도착시간으로 변경
        busStartTime += t
        // minute가 59보다 커질 경우 1 hour 추가하고 min에서 60을 빼줌
        if (busStartTime % 100 > 59) {
            busStartTime += 100
            busStartTime -= 60
        }
    }
    
    
    // 답 작성
    var hh = String(answer / 100)
    if (hh.count < 2) { hh = "0" + hh }

    var mm = String(answer % 100)
    if (mm.count < 2) { mm = "0" + mm }
    
    return hh + ":" + mm
}

# 뉴스 클러스터링 (집합, 딕셔너리)

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

 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
import Foundation

func solution(_ str1:String, _ str2:String) -> Int {
    
    // 분리한 입력문자를 저장할 컨테이너
    var splitstr1 = [String]()
    var splitstr2 = [String]()
    
    // 각 요소의 개수가 몇 개씩 있는지 기록할 딕셔너리 준비
    var set1 = [String : Int]()
    var set2 = [String : Int]()
    
    var intersectSet: Int = 0     // 교집합 개수
    var unionSet: Int = 0     // 합집합 개수
    
    
    // 입력문자를 두개씩 분리
    for i in 0..<str1.count - 1 {
        // 대문자로 변환한 스트링을 앞에서부터 두 문자씩 빼옴
        let first = String(str1[str1.index(str1.startIndex, offsetBy: i)]).uppercased()
        let second = String(str1[str1.index(str1.startIndex, offsetBy: i + 1)]).uppercased()
        
        // 문자가 알파벳일때만 배열에 추가
        if "A" <= first && first <= "Z"
            && "A" <= second && second <= "Z" {
            splitstr1.append(first + second)
        }
    }
    
    // set1 작성
    for elem in splitstr1 {
        // 각 요소 개수세기
        let count = splitstr1.filter { $0 == elem }.count
        // 딕셔너리 형태로 저장
        set1[elem] = count
    }
    
    // 입력문자를 두개씩 분리
    for i in 0..<str2.count - 1 {
        // 대문자로 변환한 스트링을 앞에서부터 두 문자씩 빼옴
        let first = String(str2[str2.index(str2.startIndex, offsetBy: i)]).uppercased()
        let second = String(str2[str2.index(str2.startIndex, offsetBy: i + 1)]).uppercased()
        
        // 문자가 알파벳이면 추가
        if "A" <= first && first <= "Z"
            && "A" <= second && second <= "Z" {
            splitstr2.append(first + second)
        }
    }
    // set2 작성
    for elem in splitstr2 {
        // 각 요소 개수세기
        let count = splitstr2.filter { $0 == elem }.count
        // 딕셔너리 형태로 저장
        set2[elem] = count
    }
    
    
    // set1에 대해 set2를 비교
    for it in set1 {
        // set1의 키값이 set2에도 있으면
        if (set2[it.key] != nil) {
            // 작은값을 교집합에 추가
            intersectSet += min(it.value, set2[it.key]!)
            // 큰 값을 합집합에 추가
            unionSet += max(it.value, set2[it.key]!)
        } else {
            // 키값이 없으면 합집합에 set1 요소만 추가
            unionSet += it.value
        }
    }
    
    
    // set2에 대해 set1을 비교
    for it in set2 {
        // set1에 대응되는 값이 없으면
        if (set1[it.key] == nil) {
            // 합집합에 set2 요소만 추가
            unionSet += it.value
        }
    }
    
    // 예외처리
    if unionSet == 0 { return 65536 }
    // 답안 작성
    intersectSet *= 65536
    return intersectSet / unionSet
}

# 프렌즈 4블록 (2차원배열, 스왑)

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

 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
import Foundation

func solution(_ m:Int, _ n:Int, _ board:[String]) -> Int {

    var isFinished = false
    var answer = 0
    
    // 입력받은 문자열배열을 2차원 캐릭터배열로 분리변환
    var newBoard = [[String]]()
    for elem in board {
        var arr = [String]()
        for e in elem {
            arr.append(String(e))
        }
        newBoard.append(arr)
    }
    
    
    while true {
        isFinished = true
        var judgeBoard = [[Bool]](repeating: Array(repeating: false, count: n), count: m)

        for row in 1 ..< m {
            for col in 1 ..< n {
                // 배열을 순회하면서 인접한 사각 요소를 가져옴
                let a = newBoard[row - 1][col - 1]
                let b = newBoard[row - 1][col]
                let c = newBoard[row][col - 1]
                let d = newBoard[row][col]
                
                // 사각 요소가 모두 일치하는지 확인
                if (a == b && b == c && c == d) {
                    if a == "-" { continue }
                    // 일치하면 다음 요소를 검색
                    isFinished = false
                    // 판정 보드에 트루라고 기록
                    judgeBoard[row - 1][col - 1] = true
                    judgeBoard[row - 1][col] = true
                    judgeBoard[row][col - 1] = true
                    judgeBoard[row][col] = true
                }
            }
        }
        
        
        // 배열을 순회하면서 true이면 newBoard값을 -로 변경
        for row in 0 ..< m {
            for col in 0 ..< n {
                if judgeBoard[row][col] {
                    newBoard[row][col] = "-"
                }
            }
        }
        

        // 삭제하고 나서 떠있는 블럭을 밑으로 떨어뜨림
        for row in 0 ..< n {
            for j in (0 ... m - 1).reversed() {
//            for j in stride(from: m - 1, through: 0, by: -1) {
                var col = j
                // 한 칸씩 내려보내다가 빈공간(-)이 없어지면 중단한다
                while (col + 1 < m && newBoard[col + 1][row] == "-") {
                    // 아래 위 보드 내용을 스왑
                    let temp = newBoard[col][row]
                    newBoard[col][row] = newBoard[col + 1][row]
                    newBoard[col + 1][row] = temp
                    
                    col += 1
                }
            }
        }
        
        // 처리가 끝났으면 종료한다
        if (isFinished) { break }
    }

    // -의 갯수를 계수한다
    for row in 0 ..< m {
        for col in 0 ..< n {
            if (newBoard[row][col] == "-") {
                answer += 1
            }
        }
    }
    
    return answer
}

# 추석 트래픽 (문자열 시간변환)

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

 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
import Foundation

func convertTime(_ input: [String]) -> [[Double]] {

    var startList = [Double]()
    var endList = [Double]()
    
    for i in input {
        let (timeStr, durationStr) = (i.split(separator: " ")[1], i.split(separator: " ")[2])
        
        // 시간 문자열을 sec 스케일의 숫자로 변환
        let timeSplit = timeStr.split(separator: ":")
        let hour = Double(String(timeSplit[0]))! * 3600
        let min = Double(String(timeSplit[1]))! * 60
        let sec = Double(String(timeSplit[2][timeSplit[2].startIndex...timeSplit[2].index(after: timeSplit[2].startIndex)]))!
        let subsec = Double("0." + String(timeSplit[2][timeSplit[2].index(timeSplit[2].startIndex, offsetBy: 3)..<timeSplit[2].endIndex]))!
        
        // 간격 가져오기
        let duration = Double(String(durationStr[durationStr.startIndex..<durationStr.index(before: durationStr.endIndex)]))!
        
        
        // 시작과 종료시간을 각각 배열화
        let endTime: Double = hour + min + sec + subsec
        let startTime: Double = endTime - duration + 0.001
        
        startList.append(startTime)
        endList.append(endTime)
    }
    
    return [startList, endList]
}

func solution(_ lines:[String]) -> Int {
    
    let converttime = convertTime(lines)
    let (startList, endList) = (converttime[0], converttime[1])
    
    var request = [Int]()
    
    // 각 시작시간과 종료시간의 직전, 직후 윈도우 요청수만을 확인
    for k in (startList + endList) {
        var count = 0
        for (i, j) in zip(startList, endList) {
            if i < k + 1 && j >= k {
                count += 1
            } else {
                count += 0
            }
        }
        request.append(count)
    }
    
    return request.max()!
}
Built with Hugo
Theme Stack designed by Jimmy