HomeAboutMeBlogGuest
© 2025 Sejin Cha. All rights reserved.
Built with Next.js, deployed on Vercel
👻
개발 기록
/
코딩테스트 스터디
코딩테스트 스터디
/
후보키

후보키

Link
https://school.programmers.co.kr/learn/courses/30/lessons/42890
Deadline
Status
Archived
Type
Hash Table
Combination
notion image
notion image

재영

결국 핵심은 조합 문제.
계속해서 N개를 뽑아내면서 조합이 유효한지를 판별한다. 이는 최소성을 만족한다.
이때, 기존에 있던 키들에 대해서 겹치는 것이 있는지를 판별한다. 이는 유일성을 만족한다.
결과적으로 이 2가지를 만족하면 키를 등록한다.
모든 반복문을 돌린 후 결과를 반한환다.
const comb = (arr, count) => { if (count === 1) return arr.map((value) => [value]); const combinations = []; arr.forEach((value, index) => { const nextArr = arr.filter((_, nextIndex) => index < nextIndex); const result = comb(nextArr, count - 1).map((res) => [value, ...res]); combinations.push(...result); }); return combinations; }; const zip = (...args) => { const res = Array.from({ length: args[0].length }, () => []); args.forEach((row) => { row.forEach((col, colIndex) => { res[colIndex].push(col); }); }); return res; }; class AlphabetKeyIndexStrategy { constructor(indexCount, caseType = 'uppercase') { if (indexCount > 26) { throw new Error('알파벳 수보다 많습니다.'); } this._indexCount = indexCount; this._caseType = caseType; } run() { const indices = Array.from({ length: this._indexCount }, (_, i) => String.fromCharCode(65 + i) ); return this._caseType === 'uppercase' ? indices : indices.map((v) => v.toLowerCase()); } } class KeyGenerator { constructor(num, Strategy = AlphabetKeyIndexStrategy) { this.Strategy = new Strategy(num); } generate() { return this.Strategy.run(); } } class Table { constructor(rawData, ColumnKeyGenerator = KeyGenerator) { this._data = rawData; this._ColumnKeyGenerator = new ColumnKeyGenerator(this.colLength); } get rowLength() { return this._data.length; } get colLength() { return this._data?.[0]?.length ?? 0; } get columnKeys() { return this._ColumnKeyGenerator.generate(); } get columns() { const dataZip = zip(...this.data); return this.columnKeys.reduce( (acc, cur, index) => ({ ...acc, [cur]: dataZip[index], }), {} ); } get data() { return this._data; } } const hasSubset = (setArr, target) => { return setArr.every( (cmpArr) => !cmpArr.every((value) => target.includes(value)) ); }; const checkIsCandidateKey = (columns, combination, rowLength) => { const res = Array.from({ length: rowLength }, () => []); for (let i = 0; i < rowLength; i += 1) { combination.forEach((key) => { res[i].push(columns[key][i]); }); } return ( new Set(res.map((v) => v.join('👋🏻🖐🏻👋🏻세상을바꾸는뱅크몰👋🏻🖐🏻👋🏻'))).size === rowLength ); }; const getAllCandidateKeys = (table) => { const result = new Set(); for (let keyCount = 1; keyCount <= table.colLength; keyCount += 1) { const combinations = comb(table.columnKeys, keyCount); combinations.forEach((combination) => { const resultArr = [...result]; if ( hasSubset(resultArr, combination) && checkIsCandidateKey(table.columns, combination, table.rowLength) ) { result.add(combination); } }); } return [...result]; }; const solution = (relations) => { const table = new Table(relations); return getAllCandidateKeys(table).length; };