ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Programmers Lv2) 후보키
    Algorithm/Programmers 2020. 8. 25. 17:06
    728x90

    출처: programmers.co.kr/learn/courses/30/lessons/42890

    분류: 카카오 블라인드 2019


    접근방식

    유일성과 최소성을 만족하는 후보키를 찾는 문제입니다.

    각 row를 유일하게 식별할 수 있다면 유일성을 만족합니다.
    이미 후보키가 포함되어 있다면 최소성을 만족하지 못합니다.

    유일성 확인

    ["a","1","4"]
    [
    "2","1","5"]
    [
    "a","2","4"]

    테이블이 다음과 같이 주어졌고 각 column을 0, 1, 2 라고 할 때,
    [0], [1], [2], [1,3] 는 모두 중복을 포함하고 있으므로 유일성을 만족하지 못해 후보키가 될 수 없고,
    [0, 1], [1, 2] 는 후보키가 될 수 있습니다.
    [1, 2, 3]은 이미 [1, 2]가 포함되어 있으므로 최소성을 만족하지 못해 후보키가 될 수 없습니다.

    2차원 배열로 주어지지만 칼럼별로 봐야 하기 때문에 뒤죽박죽... 굉장히 애를 먹었습니다.

    저는 칼럼의 인덱스만 가지고 가능한 조합  Combination을 모두 구하고 원소의 개수로 오름차순 정렬한 뒤,

    func combinationColumn(relation: [[String]], sholudSelect: Int, current index: Int, selected: [Int], combiList: inout [[Int]]) {
        
        if sholudSelect == 0 {
            combiList.append(selected)
        } else if index == relation[0].count {
            return
        } else {
            var newSelected = selected
            newSelected.append(index)
            combinationColumn(relation: relation, sholudSelect: sholudSelect, current: index+1, selected: selected, combiList: &combiList)
            combinationColumn(relation: relation, sholudSelect: sholudSelect-1, current: index+1, selected: newSelected, combiList: &combiList)
        }
    }


    각 칼럼 별 row를 모두 붙여서 Set을 만들어서 원소의 개수가 그대로인지 확인하는 방식으로 해결했습니다.

    func numberOfRowsSet(of columns: [Int], in relation: [[String]]) -> Int {
        var rows = [String](repeating: "", count: relation.count)
        for col in columns {
            for (idx, row) in relation.enumerated() {
                rows[idx] += row[col]
            }
        }
        return Set(rows).count
    }

    위의 예에서 [0, 2] 번 조합을 예로 들어보면,

    ["a4"]
    ["25"]
    ["a4"]

    와 같이 로우를 모두 붙여서 하나의 칼럼으로 만들고 Set으로 만들었습니다.
    그럼 "a4"가 중복되므로 기존의 3개가 아닌 2개로 줄어, 유일성이 깨지게 됩니다.

    최소성 확인

    이렇게 키를 찾고 나면 다음엔 최소성을 만족하는지 확인해야 합니다.

    만약 [1, 4], [0, 2], [1, 2] 의 후보키를 찾았다면,
    [1, 2, 3]은 [1, 2]를 포함하고 있기 때문에 최소성을 만족하지 못합니다. 대신 [1, 4], [0, 2]는 문제가 되지 않습니다.

    더 똑똑한 방법이 생각나지 않아 일일히 k가 조합에 모두 포함되어 있는지 확인하는 방식으로 해결했네요..

    func isUnique(combination com: [Int], keys: Set<[Int]>) -> Bool {
        for key in keys {
            var unique = false
            for k in key {
                if !com.contains(k) {
                    unique = true
                    break
                }
            }
            if !unique {
                return false
            }
        }
        return true
    }

     

    해결방법

    전체 풀이입니다.

    import Foundation
    
    // 칼럼 인덱스 조합 생성
    func combinationColumn(relation: [[String]], sholudSelect: Int, current index: Int, selected: [Int], combiList: inout [[Int]]) {
        
        if sholudSelect == 0 {
            combiList.append(selected)
        } else if index == relation[0].count {
            return
        } else {
            var newSelected = selected
            newSelected.append(index)
            combinationColumn(relation: relation, sholudSelect: sholudSelect, current: index+1, selected: selected, combiList: &combiList)
            combinationColumn(relation: relation, sholudSelect: sholudSelect-1, current: index+1, selected: newSelected, combiList: &combiList)
        }
    }
    
    // 칼럼을 하나로 합치고 Set의 개수를 확인해 유일성 확인
    func numberOfRowsSet(of columns: [Int], in relation: [[String]]) -> Int {
        var rows = [String](repeating: "", count: relation.count)
        for col in columns {
            for (idx, row) in relation.enumerated() {
                rows[idx] += row[col]
            }
        }
        return Set(rows).count
    }
    
    // 최소성 확인
    func isUnique(combination com: [Int], keys: Set<[Int]>) -> Bool {
        for key in keys {
            var unique = false
            for k in key {
                if !com.contains(k) {
                    unique = true
                    break
                }
            }
            if !unique {
                return false
            }
        }
        return true
    }
    
    func solution(_ relation:[[String]]) -> Int {
        var keys = Set<[Int]>()
        let rowCount = relation.count
        let colCount = relation[0].count
        var combiList = [[Int]]()
        
        for keyCount in 1...colCount {
             combinationColumn(relation: relation, sholudSelect: keyCount, current: 0, selected: [], combiList: &combiList)
        }
        combiList.sort(by: { $0.count < $1.count })
        
        for combi in combiList {
            // 작은것부터 하니까 keys가 combi에 전부 포함되어 있는지만 보면 됨
            guard isUnique(combination: combi, keys: keys) else { continue }
            if numberOfRowsSet(of: combi, in: relation) == rowCount {
                keys.insert(combi)
                // print(keys)
            }
        }
        
        return keys.count
    }

     

     

    배운점

    하 ... 진짜 풀다가 울뻔했습니다... ㅠㅠㅠㅠㅠ

    레벨 2인데 너무 어려웠네요... 막 풀다가 결국 다른 분들의 풀이를 참고했습니다. 조합 방식이 제일 잘 이해가 되서 조합으로 풀었고,

    비트마스킹 방식으로도 풀 수 있는 것 같아요! 이 방법은 다음에 공부해서 접근해봐야겠습니다 !!!

    오늘도 화이팅 🔥

    'Algorithm > Programmers' 카테고리의 다른 글

    Programmers Lv2) 압축  (0) 2020.08.28
    Programmers Lv2) 방금그곡  (0) 2020.08.28
    Programmers Lv2) 캐시  (0) 2020.08.24
    Programmers Lv3) 방문 길이  (0) 2020.07.10
    Programmers) Lv2 가장 큰 수  (0) 2020.05.01

    댓글

Designed by Tistory.