- 난이도 LV.2
문제 풀이
처음에는 그냥 비교 탐색으로 했다.
조건에 맞는 count 면 ++ 하여서 해당 조건에 만족하는 것을 push 시켜주었다.
function solution(info, query) {
var answer = [];
for(let q of query){
let [lan,par,car,last]=q.split("and").map(item=>item.trim())
let [foo,sco]=last.split(" ");
let count=0;
for(let i of info){
let [a,b,c,d,e]=i.split(" ");
if(lan==="-" || lan===a){
if(par==="-" || par===b){
if(car==="-" || car===c){
if(foo==="-" || foo===d){
if(+sco <= + e){
count++;
}
}
}
}
}
}
answer.push(count);
}
return answer;
}
그렇게 했을 때, 정확성은 다 맞는데 효율성에서 시간 초과가 발생한다.
다시 문제로 돌아가보면, 비교 탐색을 했을 때 50,000 * 100,000 이므로 최악의 경우 50억번을 반복해야한다.
그래서 비교 탐색을 하지 않고, 탐색하는 방법을 이진 탐색으로 하기로 했다.
- 이진 탐색 적용 풀이
처음에 이진 탐색을 어떻게 적용해야하는지 고민했다.
1. 모든 경우의 수를 넣어준다.
만약 info 가 "java backend junior pizza 150" 이라고 할 때, query에서 해당하는 조건은
- java backend junior pizza
- - backend junior pizza
- java - junior pizza
- java backend - pizza
- java backend junior -
- - - junior pizza
- - backend - pizza
- - backend junior -
- java - - pizza
- java - junior -
- java backend junior - -
- java - - -
- - backend - -
- - - junior -
- - - - pizza
- - - - -
위에 16개의 경우의 수에 가능할 것이다. 파이썬은 Combination 함수가 내장되어있는데 자바스크립트는 그렇지 않아서 불편하다 😅
16개 정도는 손으로 쓸 수 있으니 그냥 노가다로 작업했다. (만약 그 이상의 조합을 구해야한다면 함수로 짜야할 것 같다 !)
const getComb=(item)=>{
const [a,b,c,d,e]=item.split(" ");
const comb=[
`${a}${b}${c}${d}`,
`-${b}${c}${d}`,
`${a}-${c}${d}`,
`${a}${b}-${d}`,
`${a}${b}${c}-`,
`--${c}${d}`,
`-${b}-${d}`,
`-${b}${c}-`,
`${a}--${d}`,
`${a}-${c}-`,
`${a}${b}--`,
`${a}---`,
`-${b}--`,
`--${c}-`,
`---${d}`,
`----`,
]
return {
arr:comb,
score: +e
}
}
이렇게 구한 조합을 Map에 넣어준다.
const comb=new Map();
for(let i of info){
const {arr,score}=getComb(i);
for(let str of arr){
const target=comb.get(str);
comb.set(str, target ? [...target,score] : [score]);
}
}
이렇게 했을 때, query 문에서 index로 찾으면 되기 때문에 O(1)의 시간이 걸린다.
그런데 comb.set을 해줄 때, spread 문법을 사용해줬는데 그랬을 때 효율성에서 점수가 까였다.
for(let i of info){
const {arr,score}=getComb(i);
for(let str of arr){
const target=comb.get(str);
let cur;
if(!target) cur=[score];
else{
cur=target;
cur.push(score)
}
comb.set(str, cur);
}
}
그래서 더 복잡한 if/else 문으로 분리해주었다
3. score 정렬해주기
이분 탐색은 정렬이 되어있다는 조건이 중요하므로 정렬을 해주어야 한다.
for(let [key, value] of comb){
const result=value.sort((a,b)=>a-b);
comb.set(key, result);
}
4. 현재 score보다 큰 경우 찾기
for(let q of query){
let [a,b,c,last]=q.split("and").map(item=>item.trim());
let [d,score]=last.split(' ');
score = + score
}
먼저, query 같은 경우에는 ' java and backend and junior and pizza 100 ' 과 같이 and로 분리되다가 마지막에 score가 나오기 때문에 다음과 같이 처리해주었다.
const target=`${a}${b}${c}${d}`
const arr=comb.get(target);
let left=0;
let right=arr.length;
while(left<right){
let mid=Math.floor((left+right)/2);
if(arr[mid] < score) left=mid+1;
else right=mid;
}
map 에 저장되어 있는 경우의 수를 꺼내주고, 이분 탐색을 해준다.
- mid 값이 score 보다 작은 경우에는, left를 높혀서 더 높은 점수대가 search 될 수 있도록 해준다.
- mid 값이 score보다 크거나 같은 경우에는, right 값을 낮춰줘서 더 낮은 점수대가 search 될 수 있도록 해준다.
그리고, 내가 찾고 싶은 수는 score에 만족하는 최고로 낮은 idx이기 때문에
answer.push(arr.length-left);
전체 length 에서 left 값을 빼준다.
5. 런타임 해결
이렇게 했을 때, 런타임이 발생하는데 그 이유는 map에서 가져올 때 해당 경우의 수가 없을 수 있기 때문이다.
const arr=comb.get(target);
if(!arr) {
answer.push(0);
continue;
}
그래서 다음과 같은 코드를 추가하여, arr가 있는지 없는지 판단해주었다.
전체코드
const getComb = (item) => {
const [a, b, c, d, e] = item.split(" ");
const comb = [
`${a}${b}${c}${d}`,
`-${b}${c}${d}`,
`${a}-${c}${d}`,
`${a}${b}-${d}`,
`${a}${b}${c}-`,
`--${c}${d}`,
`-${b}-${d}`,
`-${b}${c}-`,
`${a}--${d}`,
`${a}-${c}-`,
`${a}${b}--`,
`${a}---`,
`-${b}--`,
`--${c}-`,
`---${d}`,
`----`,
];
return {
arr: comb,
score: +e,
};
};
function solution(info, query) {
var answer = [];
let comb = new Map();
for (let i of info) {
const { arr, score } = getComb(i);
for (let str of arr) {
const target = comb.get(str);
let cur;
if (!target) cur = [score];
else {
cur = target;
cur.push(score);
}
comb.set(str, cur);
}
}
for (let [key, value] of comb) {
const result = value.sort((a, b) => a - b);
comb.set(key, result);
}
for (let q of query) {
let [a, b, c, last] = q.split("and").map((item) => item.trim());
let [d, score] = last.split(" ");
score = +score;
const target = `${a}${b}${c}${d}`;
const arr = comb.get(target);
if (!arr) {
answer.push(0);
continue;
}
let left = 0;
let right = arr.length;
while (left < right) {
let mid = Math.floor((left + right) / 2);
if (arr[mid] < score) left = mid + 1;
else right = mid;
}
answer.push(arr.length - left);
// console.log(arr,score,mid, left,right)
}
return answer;
}
'STUDY > Algorithms' 카테고리의 다른 글
[JS] 징검다리 건너기 (0) | 2023.04.25 |
---|---|
[JS] 교점에 별 만들기 (0) | 2023.04.07 |
JS로 알고리즘하기 (0) | 2023.03.03 |
[JS | 알고리즘] BFS 와 0-1 BFS (0) | 2023.02.05 |