설명
카카오는 하반기 경력 개발자 공개채용을 진행 중에 있으며 현재 지원서 접수와 코딩테스트가 종료되었습니다. 이번 채용에서 지원자는 지원서 작성 시 아래와 같이 4가지 항목을 반드시 선택하도록 하였습니다.
인재영입팀에 근무하고 있는 니니즈는 코딩테스트 결과를 분석하여 채용에 참여한 개발팀들에 제공하기 위해 지원자들의 지원 조건을 선택하면 해당 조건에 맞는 지원자가 몇 명인 지 쉽게 알 수 있는 도구를 만들고 있습니다.
예를 들어, 개발팀에서 궁금해하는 문의사항은 다음과 같은 형태가 될 수 있습니다.
코딩테스트에 java로 참여했으며, backend 직군을 선택했고, junior 경력이면서, 소울푸드로 pizza를 선택한 사람 중 코딩테스트 점수를 50점 이상 받은 지원자는 몇 명인가?
물론 이 외에도 각 개발팀의 상황에 따라 아래와 같이 다양한 형태의 문의가 있을 수 있습니다.
즉, 개발팀에서 궁금해하는 내용은 다음과 같은 형태를 갖습니다.
- [조건]을 만족하는 사람 중 코딩테스트 점수를 X점 이상 받은 사람은 모두 몇 명인가?
문제
지원자가 지원서에 입력한 4가지의 정보와 획득한 코딩테스트 점수를 하나의 문자열로 구성한 값의 배열 info, 개발팀이 궁금해하는 문의조건이 문자열 형태로 담긴 배열 query가 매개변수로 주어질 때,
각 문의조건에 해당하는 사람들의 숫자를 순서대로 배열에 담아 return 하도록 solution 함수를 완성해 주세요.
제한사항
이 문제는 단순히 info
배열을 순회하며 완전 탐색을 하면 시간 초과가 납니다.
그래서 탐색하기 좋은 최적의 테이블을 만들고 X점 이상의 개수를 찾으면 되기 때문에
'lower_bound를 사용하면 딱이겠다'라고 감이 옵니다.
계획1 - 탐색하기 좋은 최적의 테이블 생성
const map = info.reduce((m, v) => {
v = v.split(' ');
const score = Number(v[4]);
(function f(depth, key) {
if (depth == 4) {
m[key] = m[key] || [];
m[key].push(score);
return;
}
f(depth + 1, key + '-'); // 포함시키지 않거나
f(depth + 1, key + v[depth]); // 포함시키거나
})(0, '');
return m;
}, {});
info
배열을 돌면서 각 참가자들의 정보들에 대해
포함시키거나, 포함시키지 않게 해서 모든 경우의 키 조합을 만들고
그에 해당하는 점수를 배열에 push합니다.
계획2 - 계획1에서 만든 테이블의 배열을 모두 정렬
Object.values(map).forEach((scores) => scores.sort((a, b) => a - b));
lower_bound
를 이용하려면 먼저 정렬을 해야합니다.
계획3 - lower_bound
를 이용해서 쿼리 수행
return query.map((q) => {
const key = q.replace(/ and |\s\d+$/g, ''); // 정규식으로 키 추출
const scores = map[key]; // 키에 해당하는 array
if (!scores) return 0; // array가 undefined, 즉 존재 안함
const score = Number(q.match(/\d+$/)[0]); // 정규식으로 점수 추출
let s = 0;
let e = scores.length;
while (e > s) { // lower_bound 수행
const mid = Math.floor((s + e) / 2);
if (scores[mid] < score) {
s = mid + 1;
}
else {
e = mid;
}
}
return scores.length - e;
});
이런 식으로 진행하면 시간 초과 없이 문제를 통과할 수 있습니다.
function solution(info, query) {
const map = info.reduce((m, v) => {
v = v.split(' ');
const score = Number(v[4]);
(function f(depth, key) {
if (depth == 4) {
m[key] = m[key] || [];
m[key].push(score);
return;
}
f(depth + 1, key + '-');
f(depth + 1, key + v[depth]);
})(0, '');
return m;
}, {});
Object.values(map).forEach((scores) => scores.sort((a, b) => a - b));
return query.map((q) => {
const key = q.replace(/ and |\s\d+$/g, '');
const scores = map[key];
if (!scores) return 0;
const score = Number(q.match(/\d+$/)[0]);
let s = 0;
let e = scores.length;
while (e > s) {
const mid = Math.floor((s + e) / 2);
if (scores[mid] < score) {
s = mid + 1;
}
else {
e = mid;
}
}
return scores.length - e;
});
}