Skip to content

Commit 48fa68b

Browse files
committed
[level 2] Title: 후보키, Time: 7.51 ms, Memory: 78.2 MB -BaekjoonHub
1 parent c1ff4da commit 48fa68b

File tree

2 files changed

+136
-0
lines changed

2 files changed

+136
-0
lines changed
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
# [level 2] 후보키 - 42890
2+
3+
[문제 링크](https://school.programmers.co.kr/learn/courses/30/lessons/42890)
4+
5+
### 성능 요약
6+
7+
메모리: 78.2 MB, 시간: 7.51 ms
8+
9+
### 구분
10+
11+
코딩테스트 연습 > 2019 KAKAO BLIND RECRUITMENT
12+
13+
### 채점결과
14+
15+
정확성: 100.0<br/>합계: 100.0 / 100.0
16+
17+
### 제출 일자
18+
19+
2025년 10월 14일 20:14:19
20+
21+
### 문제 설명
22+
23+
<h2>후보키</h2>
24+
25+
<p>프렌즈대학교 컴퓨터공학과 조교인 제이지는 네오 학과장님의 지시로, 학생들의 인적사항을 정리하는 업무를 담당하게 되었다.</p>
26+
27+
<p>그의 학부 시절 프로그래밍 경험을 되살려, 모든 인적사항을 데이터베이스에 넣기로 하였고, 이를 위해 정리를 하던 중에 후보키(Candidate Key)에 대한 고민이 필요하게 되었다.</p>
28+
29+
<p>후보키에 대한 내용이 잘 기억나지 않던 제이지는, 정확한 내용을 파악하기 위해 데이터베이스 관련 서적을 확인하여 아래와 같은 내용을 확인하였다.</p>
30+
31+
<ul>
32+
<li>관계 데이터베이스에서 릴레이션(Relation)의 튜플(Tuple)을 유일하게 식별할 수 있는 속성(Attribute) 또는 속성의 집합 중, 다음 두 성질을 만족하는 것을 후보 키(Candidate Key)라고 한다.
33+
34+
<ul>
35+
<li>유일성(uniqueness) : 릴레이션에 있는 모든 튜플에 대해 유일하게 식별되어야 한다.</li>
36+
<li>최소성(minimality) : 유일성을 가진 키를 구성하는 속성(Attribute) 중 하나라도 제외하는 경우 유일성이 깨지는 것을 의미한다. 즉, 릴레이션의 모든 튜플을 유일하게 식별하는 데 꼭 필요한 속성들로만 구성되어야 한다.</li>
37+
</ul></li>
38+
</ul>
39+
40+
<p>제이지를 위해, 아래와 같은 학생들의 인적사항이 주어졌을 때, 후보 키의 최대 개수를 구하라.</p>
41+
42+
<p><img src="https://grepp-programmers.s3.amazonaws.com/files/production/f1a3a40ede/005eb91e-58e5-4109-9567-deb5e94462e3.jpg" title="" alt="cand_key1.png"></p>
43+
44+
<p>위의 예를 설명하면, 학생의 인적사항 릴레이션에서 모든 학생은 각자 유일한 "학번"을 가지고 있다. 따라서 "학번"은 릴레이션의 후보 키가 될 수 있다.<br>
45+
그다음 "이름"에 대해서는 같은 이름("apeach")을 사용하는 학생이 있기 때문에, "이름"은 후보 키가 될 수 없다. 그러나, 만약 ["이름", "전공"]을 함께 사용한다면 릴레이션의 모든 튜플을 유일하게 식별 가능하므로 후보 키가 될 수 있게 된다.<br>
46+
물론 ["이름", "전공", "학년"]을 함께 사용해도 릴레이션의 모든 튜플을 유일하게 식별할 수 있지만, 최소성을 만족하지 못하기 때문에 후보 키가 될 수 없다.<br>
47+
따라서, 위의 학생 인적사항의 후보키는 "학번", ["이름", "전공"] 두 개가 된다.</p>
48+
49+
<p>릴레이션을 나타내는 문자열 배열 relation이 매개변수로 주어질 때, 이 릴레이션에서 후보 키의 개수를 return 하도록 solution 함수를 완성하라.</p>
50+
51+
<h5>제한사항</h5>
52+
53+
<ul>
54+
<li>relation은 2차원 문자열 배열이다.</li>
55+
<li>relation의 컬럼(column)의 길이는 <code>1</code> 이상 <code>8</code> 이하이며, 각각의 컬럼은 릴레이션의 속성을 나타낸다.</li>
56+
<li>relation의 로우(row)의 길이는 <code>1</code> 이상 <code>20</code> 이하이며, 각각의 로우는 릴레이션의 튜플을 나타낸다.</li>
57+
<li>relation의 모든 문자열의 길이는 <code>1</code> 이상 <code>8</code> 이하이며, 알파벳 소문자와 숫자로만 이루어져 있다.</li>
58+
<li>relation의 모든 튜플은 유일하게 식별 가능하다.(즉, 중복되는 튜플은 없다.)</li>
59+
</ul>
60+
61+
<h5>입출력 예</h5>
62+
<table class="table">
63+
<thead><tr>
64+
<th>relation</th>
65+
<th>result</th>
66+
</tr>
67+
</thead>
68+
<tbody><tr>
69+
<td><code>[["100","ryan","music","2"],["200","apeach","math","2"],["300","tube","computer","3"],["400","con","computer","4"],["500","muzi","music","3"],["600","apeach","music","2"]]</code></td>
70+
<td>2</td>
71+
</tr>
72+
</tbody>
73+
</table>
74+
<h5>입출력 예 설명</h5>
75+
76+
<p>입출력 예 #1<br>
77+
문제에 주어진 릴레이션과 같으며, 후보 키는 2개이다.</p>
78+
79+
80+
> 출처: 프로그래머스 코딩 테스트 연습, https://school.programmers.co.kr/learn/challenges
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import java.util.*;
2+
3+
class Solution {
4+
List<Set<Integer>> candidateKeys = new ArrayList<>();
5+
6+
public int solution(String[][] relation) {
7+
int colCount = relation[0].length;
8+
boolean[] visited = new boolean[colCount];
9+
10+
for (int r = 1; r <= colCount; r++) { // 조합 크기 1~N
11+
backtrack(0, 0, r, relation, visited);
12+
}
13+
14+
return candidateKeys.size();
15+
}
16+
17+
public void backtrack(int depth, int start, int target, String[][] relation, boolean[] visited) {
18+
if (depth == target) {
19+
List<Integer> cols = new ArrayList<>();
20+
for (int i = 0; i < visited.length; i++) {
21+
if (visited[i]) cols.add(i);
22+
}
23+
24+
if (isUnique(cols, relation) && isMinimal(cols)) {
25+
candidateKeys.add(new HashSet<>(cols));
26+
}
27+
return;
28+
}
29+
30+
for (int i = start; i < relation[0].length; i++) {
31+
if (!visited[i]) {
32+
visited[i] = true;
33+
backtrack(depth + 1, i + 1, target, relation, visited);
34+
visited[i] = false;
35+
}
36+
}
37+
}
38+
39+
private boolean isUnique(List<Integer> cols, String[][] relation) {
40+
Set<String> tuples = new HashSet<>();
41+
for (String[] row : relation) {
42+
StringBuilder sb = new StringBuilder();
43+
for (int c : cols) sb.append(row[c]).append("|");
44+
tuples.add(sb.toString());
45+
}
46+
return tuples.size() == relation.length;
47+
}
48+
49+
private boolean isMinimal(List<Integer> cols) {
50+
Set<Integer> current = new HashSet<>(cols);
51+
for (Set<Integer> key : candidateKeys) {
52+
if (current.containsAll(key)) return false;
53+
}
54+
return true;
55+
}
56+
}

0 commit comments

Comments
 (0)