본문 바로가기

Algorithm/level2

[ 프로그래머스 - Kotlin ] 후보키 ( 코틀린 )

728x90

( 2019 KAKAO BLIND RECRUITMENT / 후보키 )

[문제]

문제 설명

프렌즈대학교 컴퓨터공학과 조교인 제이지는 네오 학과장님의 지시로, 학생들의 인적사항을 정리하는 업무를 담당하게 되었다.

그의 학부 시절 프로그래밍 경험을 되살려, 모든 인적사항을 데이터베이스에 넣기로 하였고, 이를 위해 정리를 하던 중에 후보키(Candidate Key)에 대한 고민이 필요하게 되었다.

후보키에 대한 내용이 잘 기억나지 않던 제이지는, 정확한 내용을 파악하기 위해 데이터베이스 관련 서적을 확인하여 아래와 같은 내용을 확인하였다.

  • 관계 데이터베이스에서 릴레이션(Relation)의 튜플(Tuple)을 유일하게 식별할 수 있는 속성(Attribute) 또는 속성의 집합 중, 다음 두 성질을 만족하는 것을 후보 키(Candidate Key)라고 한다.
    • 유일성(uniqueness) : 릴레이션에 있는 모든 튜플에 대해 유일하게 식별되어야 한다.
    • 최소성(minimality) : 유일성을 가진 키를 구성하는 속성(Attribute) 중 하나라도 제외하는 경우 유일성이 깨지는 것을 의미한다. 즉, 릴레이션의 모든 튜플을 유일하게 식별하는 데 꼭 필요한 속성들로만 구성되어야 한다.

제이지를 위해, 아래와 같은 학생들의 인적사항이 주어졌을 때, 후보 키의 최대 개수를 구하라.

위의 예를 설명하면, 학생의 인적사항 릴레이션에서 모든 학생은 각자 유일한 "학번"을 가지고 있다. 따라서 "학번"은 릴레이션의 후보 키가 될 수 있다.
그다음 "이름"에 대해서는 같은 이름("apeach")을 사용하는 학생이 있기 때문에, "이름"은 후보 키가 될 수 없다. 그러나, 만약 ["이름", "전공"]을 함께 사용한다면 릴레이션의 모든 튜플을 유일하게 식별 가능하므로 후보 키가 될 수 있게 된다.
물론 ["이름", "전공", "학년"]을 함께 사용해도 릴레이션의 모든 튜플을 유일하게 식별할 수 있지만, 최소성을 만족하지 못하기 때문에 후보 키가 될 수 없다.
따라서, 위의 학생 인적사항의 후보키는 "학번", ["이름", "전공"] 두 개가 된다.

릴레이션을 나타내는 문자열 배열 relation이 매개변수로 주어질 때, 이 릴레이션에서 후보 키의 개수를 return 하도록 solution 함수를 완성하라.

제한사항

  • relation은 2차원 문자열 배열이다.
  • relation의 컬럼(column)의 길이는 1 이상 8 이하이며, 각각의 컬럼은 릴레이션의 속성을 나타낸다.
  • relation의 로우(row)의 길이는 1 이상 20 이하이며, 각각의 로우는 릴레이션의 튜플을 나타낸다.
  • relation의 모든 문자열의 길이는 1 이상 8 이하이며, 알파벳 소문자와 숫자로만 이루어져 있다.
  • relation의 모든 튜플은 유일하게 식별 가능하다.(즉, 중복되는 튜플은 없다.)

입출력 예시

relation result
[["100","ryan","music","2"],["200","apeach","math","2"],["300","tube","computer","3"],["400","con","computer","4"],["500","muzi","music","3"],["600","apeach","music","2"]] 2

[풀이]

키로 지정할 열의 조합을 순서대로 비트를 이용하여 정한 후 해당 키로 값을 만들경우 유일한지 확인하는 isUnique함수를 구현하면 됩니다.

isUnique함수에서는 유일한 키인지 HashSet을 이용하여 판별합니다.


[코드]

class Solution {
    fun solution(relation: Array<Array<String>>): Int {
        // field의 모든 조합을 저장할 list
        val keyFieldSet = mutableListOf<Int>()
        val rowLen = relation.size
        val colLen = relation[0].size
        // 모든 field의 조합은 field의 항목을 0과 1로 포함관계를 표시했을 경우
        // 2^(field의 개수) 임으로 1을 왼쪽으로 colLen 만큼 이동한 만큼 반복문 실행
        for(i in 1 until (1 shl colLen)){
            // keyFieldSet의 항목 중에 i와 and를 했을 경우 나온 값을 2진수로 표현했을 경우 1인 항목이 포함할 field인데
            // 해당 항목이 it와 동일한 경우 이미 key가 있는 것으로 반복문 건너뜀
            if(keyFieldSet.any{ (i and it) == it }) continue
            // 만약 해당 field의 값으로 key를 만들경우 유니크하다면 keyFieldSet에 해당 숫자 추가
            if(isUnique(i, rowLen, colLen, relation)) keyFieldSet.add(i)
        }
        // 위 조건을 모두 통과한 keyFieldSet의 개수를 리턴
        return keyFieldSet.size
    }

    fun isUnique(fieldSet:Int, rowLen:Int, colLen:Int, relation:Array<Array<String>>):Boolean{
        val hs = HashSet<String>()

        for(row in 0 until rowLen){
            var dataSet = ""
            for(col in 0 until colLen){
                if(fieldSet and (1 shl col) != 0){
                    // 선택한 열에 해당하는 값을 dataSet에 추가
                    dataSet += relation[row][col]
                }
            }
            // 계산한 dataSet이 있을경우 false리턴
            if(hs.contains(dataSet)) return false
            // 계산한 dataSet이 없을경우 set에 해당 값 추가
            else hs.add(dataSet)
        }
        // 여기까지 오면 겹치는 데이터가 없는 것임으로 true리턴
        return true
    }
}

문제 링크

 

코딩테스트 연습 - 후보키

[["100","ryan","music","2"],["200","apeach","math","2"],["300","tube","computer","3"],["400","con","computer","4"],["500","muzi","music","3"],["600","apeach","music","2"]] 2

programmers.co.kr

제가 잘못 알고 있거나 잘못된 부분이 있을 경우 알려주시고 추가로 궁금한 점 있으신 분들도 댓글이나 메일 주시면 성실히 답변해 드리겠습니다.🧑🏻‍💻

감사합니다~😄

728x90