Skip to content

Latest commit

 

History

History
192 lines (129 loc) · 6.73 KB

20221207-lv-2-큰 수 만들기.md

File metadata and controls

192 lines (129 loc) · 6.73 KB

문제 설명

어떤 숫자에서 k개의 수를 제거했을 때 얻을 수 있는 가장 큰 숫자를 구하려 합니다.

예를 들어, 숫자 1924에서 수 두 개를 제거하면 [19, 12, 14, 92, 94, 24] 를 만들 수 있습니다. 이 중 가장 큰 숫자는 94 입니다.

문자열 형식으로 숫자 number와 제거할 수의 개수 k가 solution 함수의 매개변수로 주어집니다. number에서 k 개의 수를 제거했을 때 만들 수 있는 수 중 가장 큰 숫자를 문자열 형태로 return 하도록 solution 함수를 완성하세요.

제한 조건

  • number는 2자리 이상, 1,000,000자리 이하인 숫자입니다.
  • k는 1 이상 number의 자릿수 미만인 자연수입니다.

입출력 예

| number | k | return | | "1924" | 2 | "94" | | "1231234" | 3 | "3234" | | "4177252841" | 4 | "775841" |

첫 번쨰 풀이

Counter 구조 이용해서 풀이하려고 했습니다. 하지만 해당 방법으로 풀게되면 각 자리수의 합이 최대인 수가 나오지, 가장 큰 수가 아니었습니다.

image

단일 숫자가 작더라도 맨끝에 있는 1을 지우는 것보다는 맨 앞에 있는 4를 지우는 것이 효율적인 순간이 있습니다. 이 경우에는 단일 우선순위 Counter가 아니라 이중 우선순위 Counter로 풀어야 하지 않나 싶습니다......

function solution(nums, k) {

    // [설명] 각 자리수가 몇개나 있는지 계산하여 CounterMap형태로 반환
    const counterMap = getCounterMap(nums);
    
    // [설명] 0 부터 한칸씩 숫자를 키워가면서 무엇을 지워야하는지 연산하여, CounterMap형태로 반환
    const removeTargetMap = getRemoveTargetMap(counterMap, k);
    
    // [설명] nums에서 하나씩 꺼내서 removeTargetMap에 있는 값을 기준으로 삼아서 무시하거나, stack(string)안에 하나씩 넣고 완성
    let stack = '';
    for (const numStr of nums) {
        const num = +numStr;
        
        const isRemovedTarget = removeTargetMap.has(num);
        
        const removedTargetCount = removeTargetMap.get(num);
        const isLastRemovedTarget = removedTargetCount === 0;
        
        if (isRemovedTarget && !isLastRemovedTarget) {
            removeTargetMap.set(num, removedTargetCount - 1);
        } else {
            stack += num;
        }
    }
    
    return stack;
    
    
}

function getCounterMap(nums) {
    
    /**
     * Counter는 Dictionary형태의 자료 구조의 일부로 Item의 수를 세는 클래스입니다.
     * Java, Python과는 달리 JavaScript에서는 이와같이 수동으로 구현해야 합니다.
     */
    const counterMap = new Map();
    for (const numStr of nums) {
        
        const num = +numStr;
        const hasNum = counterMap.has(num);
        
        if (hasNum) counterMap.set(num, counterMap.get(num) +1);
        else counterMap.set(num, 1);
    }
    return counterMap;
}

function getRemoveTargetMap(counterMap, deleteCount) {
    
    let accDeleteCount = 0;
    const removeTargetMap = new Map();
    for (let num = 0; num < 10; num ++) {
        
        const hasNum = counterMap.has(num);
        
        if (hasNum) {
            
            const numCount = counterMap.get(num);
            const remainingDeleteCount = deleteCount - accDeleteCount;
            
            if (remainingDeleteCount >= numCount) {
                
                accDeleteCount += numCount;
                removeTargetMap.set(num, numCount);
                
            } else {
                
                if (accDeleteCount === deleteCount) break;
                
                accDeleteCount += remainingDeleteCount;
                removeTargetMap.set(num, remainingDeleteCount);
                
            }
        }
        
    }
    
    return removeTargetMap
    
}

두 번째 풀이

Counter 자료구조와 정렬방법 같은데서 나오는 Item의 수를 세는 특성이 이 문제를 푸는데 큰 도움이 되지 않는 것 같아요.

그래서 그냥 반복문 통째로 돌렸는데,,, 시간 초과가 나왔습니다.
아마도 로직에서 배열을 만드는 방식 자체가 문제인 것 같아요.

길이가 최대 1_000_000인 경우 하나씩 지워가면서 만들면 1_000_000 > 1(최악의 경우)까지 전부 배열을 만들어야해요.

일반적으로 splice가 훨씬 성능 열악하긴 하지만, slice 자체도 배열 길이가 길면 느린 것은 똑같기 떄문이지 않을까 싶어요...

image

function solution(nums, k) {
    
    const numsArr = nums.split('');
    
    let cnt = 0;
    while(cnt++ < k) {
        for (let idx = 0; idx < numsArr.length - 1 ; idx++) {
            const nowVal = numsArr[idx];
            const nextVal = numsArr[idx + 1];
            
            if (nowVal < nextVal) {
                numsArr.splice(idx, 1);
                break;
            }
        }
    }
    
    return numsArr.slice(0, nums.length - k).join('');
    
}

세 번쨰 풀이

시간이 한 시간이 넘어가서 해설된 코드를 참고했는데, stack 이나 counter같은 특정 자료구조를 너무 사용하는 것은 지양해야 할 것 같네요.

뭔가 생각이 닫히는 기분이에요. ㅠㅠ

function solution(number, k) {

    const lifo = [];
    for (const nowNum of number) {
        
        /**
         * 1924의 경우,
         *
         * 1회차 lifo가 아무것도 없으므로 바로 1이 들어갑니다.
         *
         * 2회차 lifo에 무언가 있으므로, lifo의 끝값인 1과 현재값 9를 비교합니다.
         *      신규 값(9)이 더 크기 때문에 lifo의 마지막 값을 지우고 9를 넣어줍니다.
         *
         * 3회차 lifo에 무언가 있으므로, lifo의 끝값인 9와 2를 비교합니다.
         *      신규 값(2)이 더 작기 때문에 lifo의 마지막 값을 유지하고 2를 넣어줍니다.
         *
         * 4회차 lifo에 무언가 있으므로, lifo 의 끝값인 2와 4를 비교합니다.
         *      신규 값(4)이 더 크기 떄문에, lifo의 끝값인 2를 지우고 4를 넣어줍니다.
         */
        while(lifo.length > 0 && lifo[lifo.length - 1] < nowNum && k > 0) {
            k--;
            lifo.pop();
        }
        lifo.push(nowNum)
        
    }
    
    return lifo.slice(0, number.length - k).join('');
    
}

image