고득점 KIT의 그리디 문제 6개 중 세 번째 문제인 큰 수 만들기를 풀어봤습니다.

아이디어를 생각하기 굉장히 까다로운 문제였습니다.

처음에 완전 잘못 생각해서 2~3시간을 날렸습니다.. 분명 맞는 것 같은데 통과를 못해서.. 눈물을 머금고 아예 갈아 엎었습니다.

그렇다면 바로 문제 해설로 넘어가시죠!

 

출처 : 프로그래머스 코딩테스트 연습 고득점 KIT, programmers.co.kr/learn/courses/30/lessons/42883

 

코딩테스트 연습 - 큰 수 만들기

 

programmers.co.kr

목차


    0. 문제 설명

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

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

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

     

    0.0. 제한 조건

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

    0.1. 입출력 예

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

     


    1. 풀이 과정

    매우 친절하게 주석에 한줄 한줄 달아놓았기 때문에 간략하게 설명하..면 안되겠죠?

     

    알고리즘 자체는 크게 어렵지 않지만, 처음에 떠올리는 것이 굉장히 어려웠습니다.

     

    먼저 string 형태의 answer의 크기는 number.size() - k입니다.

    이걸 이용해서, for문을 number.size()-k만큼 돌린 후에 한번에 한글자씩 answer에 넣으면 무언가 될 것 같다는 생각이 들기 시작합니다. 이 부분이 중요합니다.

     

    이 for문 안에서는 한 번 돌 때마다 특정 범위만큼을 검색해야합니다. 정답 글자수가 number.size()-k로 정해져있기 때문에 answer을 완성시키기 위해서는 뒤에 최소한의 값들을 남겨놓아야 합니다. 그래서 index가 k+i인 number값까지만 확인합니다. 기본적으로 뒤에 k-1개 만큼의 수를 남기고, 추가적으로 앞에서 이미 answer에 넣은 값이 존재하므로 그 값의 수만큼은 남길 필요가 없습니다. 그리고 이 값은 for문이 돌아간 횟수, 즉 i와 일치합니다. 따라서 k+i까지 조사합니다. (등호가 들어갑니다. 만약 k가 2고 number이 4자리라고 할 때 정답은 2자리가 나와야하는데, 뒤에 1개만 남기는 경우 0,1,2의 index를 조사해야하죠? 등호가 필요한 이유를 직관적으로 볼 수 있는 케이스가 되겠습니다!)

    그리고 for문 안 for문에서 maxint와 maxidx를 구해줍니다.

     

    이 작업을 완료해서 최댓값과 최댓값의 index를 구했다면, 최댓값을 answer에 넣고, startidx를 최댓값의 index의 다음 index로 넣어줍니다. 이 과정을 통해 다음 바깥for문을 돌릴 때 안쪽 for문에서 최댓값 다음 수부터 조사하게 됩니다.

     

    ex) number = "15243", 3 이면 정답 글자수는 number.size()-k인 2가 되고, 첫 번째는 1,5,2,4를 조사합니다.(이 중에 1개가 선택되고, 앞으로 최소 1개 더 골라야 하므로 뒤에 1개의 수를 남겨야합니다) 5가 가장 큰 수이므로 answer에 5를 넣습니다. 그 다음엔 5 다음에 위치한 2부터 순서대로 2,4,3을 조사합니다.(k는 3이고, i는 1이므로 index가 4인 3까지 전부 조사합니다.) 이 중에서 4가 가장 큰 수이므로 4를 answer에 넣습니다. for문이 끝나고 answer값인 "54"를 반환합니다.

     


    2. 소스 코드

    2.0. 정답 코드

    #include <string>
    #include <vector>
    
    using namespace std;
    
    string solution(string number, int k) {
        string answer = "";
        // answersize만큼 for문을 돌려서 한 번에 한 글자씩 answer를 따오고, 한 번 돌릴 때마다 startidx부터 k+i까지 돌린다.
        // 이렇게 한도를 두고 돌려야 answersize만큼의 answer 글자들을 뽑아올 수 있기 때문이다.
        int answersize = number.size() - k; // return값의 길이는 number의 사이즈에서 k를 뺀 것
        int startidx = 0; // 시작 index
        for(int i = 0; i < answersize; ++i){ // answersize만큼 반복
            char maxint = number[startidx]; // 처음에는 startidx의 값이 최대값
            int maxidx = startidx; // 처음에는 maxidx가 startidx
            for(int j = startidx; j <= k+i; j++){ // startidx부터 k+i까지 확인해서 max값 찾음 -k
                if(maxint < number[j]){ // 더 큰 값이 나오면 그 값의 index와 number를 저장
                    maxint = number[j];
                    maxidx = j;
                }
            }
            startidx = maxidx + 1; // 다음번에는 maxindex + 1에서부터 시작
            answer += maxint; // answer에 가장 큰 수를 넣는다
        }
        
        return answer;
    }

    2.1. 시행착오를 겪은 틀린 코드

    #include <string>
    #include <vector>
    
    using namespace std;
     
    string solution(string number, int k) {
    
        string answer = "";
        // 2개의 수를 비교해서 앞 수가 더 크면 answer에 넣고, 뒷 수가 더 크면 앞 수를 버린다. 만약 다 빼지 못한 상황에서 끝까지 다 돌았다면 처음부터 다시 돈다.
        // 지워야 하는 수의 남은 수를 k에서 빼나감
    
        int idx = 0;
        while(1){
            for(int i = 0; i < number.size(); ++i){
                if(number[i] >= number[i+1]){
                    answer += number[i];
                }
                else
                    --k;           
    
                if(k == 0 && i != number.size()-1){
                    for(int j = i+1; j < number.size(); ++j){
                        answer += number[j];
                    }
                    return answer;
                }
                else if(k == 0 && i == number.size()-1)
                    return answer;
            }
            number = answer;
            answer = "";
        }
        return answer;
    }

    그리디라고 해서 앞 두 수를 비교하는 방법을 생각했는데, 위 테스트5 예시를 통과하지 못해서(무한루프에 걸려서 시간초과가 납니다) 전부 갈아엎게됩니다. 저같이 실수하지 마시라고 올려봅니다.. ㅋㅋㅋㅋ


    3. 후기

    잘못된 알고리즘을 시도했을 때 얼마나 시간을 버릴 수 있는가(...)를 적나라하게 알 수 있었던 그런 문제였습니다.

     

    집에서 푼 문제가 아니다보니 종이에 알고리즘을 생각하기보다 서둘러 코드를 짜는 데 몰두한 것이 원인이 아닐까 싶습니다. 알고리즘 문제를 풀 때는 어떤 방식으로 해야 문제가 해결되는 지 정확하게 파악하는 것이 코드를 구현하는 것보다 훨씬 중요하다는 것을 깨닫게 된 좋은 시간이었습니다! 내 24시간 ㅠ

    반응형
    • 네이버 블로그 공유하기
    • 네이버 밴드에 공유하기
    • 페이스북 공유하기
    • 카카오스토리 공유하기