문제: https://programmers.co.kr/learn/courses/30/lessons/42890
주어진 데이터를 구분할 수 있는 후보키가 몇개인지 구하는 문제이다.
전체적인 로직은 위와 같이 간단하다. 하지만 이를 구현하기 위해서는 아래의 두가지 함수구현이 필요하다.
powerSet
. 멱집합 구하는함수멱집합을 구하는 법은 멱집합 구하기 포스팅에서 소개했다.
단, 이 문제에서는 2개의 filter가 필요하다.
1. 멱집합에는 공집합 즉, 빈배열 []을 제거해준다.
2. 후보키 조건 중 최소성이 있기 때문에 배열의 길이가 작은 것부터 판별해줘야 되기 때문에 정렬을 해줘야한다.
checkOverlap
. 후보키 판별 함수후보키로 들어오는 속성들을 문자열로 바꿔서 setArr
에 넣는다. (setArr.add(문자열)
)
후보키는 유일성 속성에 의해 중복이 일어나면 안된다.
setArr
에 후보키들을 문자열로 연결한 값이 있다면(setArr.has(문자열)
) 중복이 있다는 뜻이기 때문에 false
를 반환한다.
checkOverlap(부분집합)
이 true일 경우 현재 부분집합을 포함하고 있는 모든 부분집합을 제거해줘야한다. (최소성 때문)
filter
를 이용해 checkOverlap
에서 true를 반환한 부분집합을 포함하는 부분집합은 모두 제거해준다.
//caseIndexArr: 모든 부분집합을 갖고있는 배열
//nowCase : 후보키 검사를 한 부분집합
caseIndexArr = caseIndexArr.filter((idxArr) => {
//지금 nowCase의 인덱스를 모두 포함하고있는 caseIndexArr의 요소를 제외
for (let idx of nowCase) {
if (!idxArr.includes(idx)) return true;
}
return false;
});
function solution(relation) {
let answer = 0;
//Attribute의 인덱스 배열
const indexArr = Array.from({ length: relation[0].length }, (_, idx) => idx);
//Attribute의 인덱스배열의 모든 부분집합을 구한다.
let caseIndexArr = powerSet(indexArr);
while (caseIndexArr.length) {
const nowCase = caseIndexArr[0];
//nowCase에 있는 index의 Attribute로만 배열을 만든다.
const checkRelation = relation.map((v) =>
v.filter((_, idx) => nowCase.includes(idx))
);
//후보키 체크
if (checkOverlap(checkRelation)) {
answer++;
caseIndexArr = caseIndexArr.filter((idxArr) => {
//지금 nowCase의 인덱스를 모두 포함하고있는 caseIndexArr의 요소를 제외
for (let idx of nowCase) {
if (!idxArr.includes(idx)) return true;
}
return false;
});
} else {
//nowCase제거
caseIndexArr.shift();
}
}
return answer;
}
function checkOverlap(arr) {
const setArr = new Set();
for (let x of arr) {
if (setArr.has(x.join(""))) return false;
else setArr.add(x.join(""));
}
return true;
}
//모든 부분집합 (멱집합)
function powerSet(arr) {
let check = new Array(arr.length).fill(0);
let powerSetArr = [];
const dfs = (depth) => {
if (depth === check.length) {
powerSetArr.push(arr.filter((_, idx) => check[idx]));
} else {
check[depth] = 1;
dfs(depth + 1);
check[depth] = 0;
dfs(depth + 1);
}
};
dfs(0);
powerSetArr.sort((a, b) => a.length - b.length);
powerSetArr = powerSetArr.filter((v) => v.length);
return powerSetArr;
}
문제를 풀면서 멱집합을 구현하는 방법도 익히고 포스팅까지 하게 됐다.
문제를 해결한 후 프로그래머스에 올라온 다른 사람의 풀이를 보았다.
내가 푼 방식과 다르게 다른 사람들의 답안을 보니 <<라는 비트연산자? 도 사용했던데 아직 어떻게 해결한 것인지는 모르겠다. 내코드보다 훨씬 간결해 보이던데 공부해봐야겠다.