https://school.programmers.co.kr/learn/courses/30/lessons/150368

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

 

이모티콘의 최대 개수가 7개, 할인율의 종류가 4개이므로 최대 4^7 경우의 수를 탐색해야했다.

재귀를 써본적이 없어서 7중 for문을 써서 풀려고 했다 😅 하지만 이모티콘의 개수에 따라 for문을 다르게 중첩해야 하기에 그렇게 풀기는 어려웠다.

 

재귀를 써서 DFS로 풀기로 하고 함수를 작성했다.

<로직>

할인율의 조합을 담는 rates 배열을 가지고 재귀호출을 하면서, rates 배열의 모든 경우의 수를 탐색.

재귀함수가 처음 호출될 때는 rates배열이 다 0으로 초기화되어있다. 호출 될 때마다, 현재 index에 할인율을 지정된 할인율을 담음. 지정된 할인율은 재귀호출의 파라미터로 넘어오는데, 0~3의 인덱스로 각 RATES[idx]로 표현된다.

할인율을 담았으면 현재 길이를 확인한다. cur + 1 == emoticons.length  이면 재귀를 종료하고 문제에서 요구한 계산 실행.

아직 length가 부족하면 다음 할인율을 담아야하니 재귀호출을 한다. 이 때, 현재 rates의 배열에서 파생되는 모든 경우의 수(다음거 10, 20, 30, 40)를 탐색해야하므로 모든 경우의 수에 대해 재귀호출을 한다. 하나의 재귀 프로세스는 rates가 꽉차서 계산이 실행될때만 종료되기 때문에, 메모리걱정은 안해도 된다.\

문제에서 요구하는 계산 = 그냥 할인율,이모티콘 곱해서 가격구하고 몇명이 사는지, 그리고 최대인원이 살 때 매출 얼마인지. ==> MAXREG 변수가 최대로 갱신될 때 MAXREV 갱신. MAXREG가 동률일 때 MAXREV 비교

 

<구현>

 

 

DFS를 처음 짜봤는데, search함수 내에서 calculate라는 함수를 미리 넣어놓고, 나중에 짰다. 이렇게 하니까 search함수의 재귀 종료 시점에서 어떤 계산이 일어나야하는지, 그 때 필요한 파라미터는 뭐가있는지, 어떤 반환값을 가져야하는지를 미리 생각해볼 수 있어서 좋은 것 같다.

 

import java.util.*;
class Solution {
    public static int MAXREG = 0;
    public static int MAXREV = 0;
    public static int[] RATES = {10, 20, 30, 40};
    
    public int[] solution(int[][] users, int[] emoticons) {
        int[] answer = new int[2];
        int[] rates = new int[emoticons.length];
        for(int i = 0; i< RATES.length; i++){
            search(users, emoticons, rates, 0, i);
        }
        
        answer[0] = MAXREG;
        answer[1] = MAXREV;
        return answer;
    }
    
    public static void search(int[][] users, int[] prices, int[] rates, int cur, int idx){
        rates[cur] = RATES[idx];
        if (cur + 1 == rates.length){
            int[] infos = calculate(users, prices, rates);
            if (infos[0] > MAXREG){
                MAXREG = infos[0];
                MAXREV = infos[1];
            }
            else if (infos[0] == MAXREG){
                MAXREV = Math.max(MAXREV, infos[1]);
            }
        }
        else{
            for(int i = 0; i<RATES.length; i++){
                search(users, prices, rates, cur + 1, i);
            }
        }
    }
    
    public static int[] calculate(int[][] users, int[] prices, int[] rates){
        int[] ans = {0,0};
        
        for (int[] user:users){
            int buyrate = user[0];
            int buylimit = user[1];
            int pricesum = 0;
            for(int i = 0; i<prices.length; i++){
                if(rates[i] >= buyrate){
                    pricesum += (100 - rates[i]) * prices[i] / 100;
                }
            }
            if (pricesum >= buylimit){
                ans[0] += 1;
                pricesum = 0;
            }
            else{
                ans[1] += pricesum;
            }
        }
        return ans;
        
    }
}

https://school.programmers.co.kr/learn/courses/30/lessons/64065

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

 

 

열받게도 문제를 또 잘못 읽어버린 ㅡㅡ (정답 풀이 맨 아래)

 

↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓삽질의 시작↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓

<문제 이해>

주어지는 집합의 size가 순차적으로 1 -> 2 -> 3 ->4 .... 순이므로 (<== 삽질의 근원), n번째에 주어지는 집합에서 n-1번째 집합을 빼고 남는 원소가 n번째에 주어진 원소가 될 것이다. n 번째에 주어진 원소를 An이라고 하면, 결과로 반환해야하는 튜플은

(A1, A2, A3, A4.....)가 되어야 함. 즉, 주어진 Set들의 Set을 돌면서 이전 Set에 추가된 원소들을 찾으면 될 것.

 

<풀이 구상>

1. input[i]를 돌면서 input[i-1]의 원소들을 하나씩 지워나가고, 남는 원소를 FinalSet에 추가

    ==> 각 시행마다 input[i]의 길이 * input[i-1]의 길이를 탐색하므로 시간 오래걸릴 듯

 

2. input[i]와 input[i-1]를 Set으로 만들어주고, Set끼리의 연산

   ==> 파이썬처럼 Set - Set 연산이 가능한지 알아봐야함

   ==> int[]를 Set으로 만드는 것 자체가 오래걸리지 않을까 ?

   ==> 문제에서 중복이 없음을 보장하고 있는데 Set을 쓰는게 맞는가?

 

<구현가능성>

===============문제를 확인해보니, 주어지는 input이 배열이 아니라 String임============

★String 처리하여 각 input을 받아올 방법 필요

 

-파이썬처럼 Set - Set 연산이 가능한지 ?  ==> .removeAll(c) 메서드로 가능

AbstractSet의 removeAll 메서드

myself.removeAll(Collection c) ==> 내 자신의 원소가 collection c에 있으면 remove.

결국 iterator를 끝까지 도는 것은 매한가지다. 효율은 그닥일 듯. 이 함수를 쓰려면 input string s 를 거꾸로 읽는게좋겠다.

번외로, retainAll이라는 메서드도 있다.

 

==> 어차피 돌거면 그냥 앞에서부터 읽으면서, 그동안 나온 숫자를 모아놓은 SET을 유지   (=> 어차피 중복X 보장이고, 순서를 알아야하므로 List로 하기로 함)하고, i번째 집합의 원소마다 contains를 확인하여 없으면 answer에 붙이고 없으면 pass하도록 하는게 나을 것 같다고 생각함

 

-String을 읽으면서 {와 } 사이의 숫자를 읽는 방법이 필요함

buffer라는 개념이 생각남  ==> 추후 공부하여 정리하기

버퍼는 데이터를 한 곳에서 다른 한 곳으로 전송하는 동안 일시적으로 그 데이터를 보관하는 메모리의 영역

자세히는 모르지만, 이런 방법을 생각했다

1. {를 만나면 버퍼를 연다 

2. ,를 만나면 버퍼에 저장된 String을 숫자로 변환해 기존 Set에 있는지 검사

3. Set에 없으면 Set에 넣고, 진행 // Set에 있으면 버퍼를 닫고 다음 }까지 진행

 

이를 위해 buf라는 char[] 배열을 선언하였고, 이 배열의 내용물을 Int로 바꾸는 메서드와 buffer를 clear해주는 메서드를 작성하였다. ==> size를 저장하는 변수를 관리해야하는데, 어디 선언해야 예쁜지 몰라서 그냥 List<Character>로 진행

<구현>

 

<문제>

1. 내 Buffer를 String으로 바꾸는 방법에 대한 문제

buffer를 List<Character>로 구현했더니, buffer의 내용물을 String으로 바꿔주는 과정에서

Character List에 담긴 2가 String "2"로 변환되지 않고, "[2]"로 변환되는 문제가 있었다.

 buf의 타입을 바꿔줘야 할 것 같았지만, 일단 코드 작동을 확인하기 위해 정규표현식으로 [와 ]를 벗겨주었더니, 

첫번째 예시에 대해서 통과하여 일단 로직이 잘못되지는 않았다는 걸 알 수 있었다. 그러나,

한자리수를 초과하는 숫자에 대해서 "20"과 같은 String으로 나오는 것이 아니라 "2, 0"으로 나와 숫자로 변환이 안됐다.

buffer.toString()이   "[2, 0]"을 return 했다는 얘기였다.

String.valueOf(buf) 도 마찬가지였다.

String.valueOf() 메서드는 파라미터로 Object가 들어왔을 때, object가 null이 아니면 object의 toString() 함수를 호출한다고 되어있다. 따라서 String.valueOf(buf) ==  buf.toString() 메서드 (현재 buf는 List<Character>인 Object 니까)

List의 toString() 메서드를 보면,

List.toString()

사람이 이 Object에 뭐가 담겨있는지 한 눈에 볼 수 있도록 변환하도록 되어있다.

ArrayList의 element type은 Map이 될 수도 있고, Integer가 될 수도 있고, 어떤 객체든 올 수 있다.

만약 [1, 2, 3, 4, 5, 6]이 List로 담겨있을 때 내가 바랐던 것처럼 하나의 String에 이 요소들을 모두 모으게 되면,

"123456"이 되어 이 List가 한 자리 숫자들을 하나의 element로 담고있다는 정보가 없어질 것이다. 타입이 Map이거나 다른 사용자가 정의한 Class라면 더 어지러울 것이므로, toString()의 return값은 "[요소1, 요소2]" 이런식의 String으로 반환하여 로그를 찍어볼 때 한 눈에 볼 수 있게 해둔 것 같다.

★char[]같은 경우 각 element에 문자가 딱 하나씩 담긴게 보장이 되므로, "abcde"같은 string으로 반환해준다.

int, char, long, float, double, boolean, char[]에 대해서는 String 클래스 안에 정의되어있지만,

int[] 타입의 ints의 경우 Object로 간주해 int[]에 override 되어있는 toString()함수를 호출하게 하는 것을 보면, 숫자 하나, 문자하나 또는 char[]과 같이  헷갈릴만한 소지가 없는 경우에만 String.valueOf를 정의하고 있는 것을 알 수 있다.

 

<문제 해결>

방법1. buf를 List<Character>로 유지할 경우 : StringBuilder 클래스를 사용한다.

방법2. buf를 char[] 로 바꿔줄 경우 : toString()으로 변환 가능. 대신, List의 이점을 포기해햐함

                                                                                         ==>buffer size 저장, buffer clear 간편

 

조금 귀찮아도 방법 2로 하기로 했다.

 

buflen 추가하여 버퍼를 관리

 

<더 큰 문제>

또 문제를 똑바로 안읽었다. input = {{집합1}, {집합2}, {집합3}.... } 에서 input 역시 집합이므로 안에 원소가 길이가 바뀔 수가 있다는 것......... 나는 예시1만 보고 냅다 로직을 구상해버린 것이었다.😭😭

어쩐지 쉽더라 .... 정신적 충격이 심해 조금 쉬었다가 해야겠다..

 

============================================================================================

 

<새로운 풀이>

 

정신이 말짱해지고 오니 왜 저렇게 했는지 모르겠다. 난 정규표현식과 Comparator를 아는데 !!

 

1.input s 에서 처음 등장하는 {{와 마지막에 등장하는 }}를 지워준다.

2.split( " },{ ")로 각 내부집합을 받아온다

3. 2에서 받아온 내부집합은 String[] 인데, custom Comparator를 사용하여 길이순으로 정렬해준다.

4.내부집합의 원소들을 돌면서 Set에 있는지 확인하고, 없으면  tuple에 넣어준다

 

import java.util.*;
class Solution {
    public static int[] solution(String s) {
        List<Integer> tuple = new ArrayList<>();

        String regex = "^\\{\\{|}}$";  // s 시작의 {{, 끝의 }}를 지워줌
        String[] toarr = s.replaceAll(regex,"").split("},\\{");
        Arrays.sort(toarr, new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                return o1.length() - o2.length();
            }
        });

        for(String set:toarr){
            String[] nums = set.split(",");
            for(String num:nums){
                int intNum = Integer.parseInt(num);
                if (!tuple.contains(intNum)){
                    tuple.add(intNum);
                    break;
                }
            }
        }
        int[] answer = new int[tuple.size()];
        for(int i = 0; i< tuple.size(); i++){
            answer[i] = tuple.get(i);
        }

        return answer;
    }
}

 

처음 했던 방식보다 훨씬 깔끔하고 쉽게 풀렸다. 애초에 문제를 잘못 이해 했을 경우에도 이렇게 하는게 맞았는데, 깊이 생각 안하고 냅다 풀어버리니까 버퍼에 집착해서 이상하게 풀게 됐던 듯.

test case에서 걸려서 다행이지 hidden case에만 걸렸으면 진짜 오래 고민했을 것 같다.

 

제출 후에 다른사람들의 풀이를 보니 비슷한 풀이가 많았는데, 내 코드를 줄일 여지를 몇 개 찾아 적어놓는다

1. Comparator를 람다식으로 작성 ==> 가독성 좋아보임 (어차피 간단한 comparator니까)

2. java.util.regex의 Pattern과 Matcher를 활용해 input s에서 숫자만 찾고. matcher의 find와 group 활용

     ==> 추후 regex 공부할 때 알아보고 정리하기

3. Set.add에는 boolean 반환값이 있다. List로 하지 않더라도 if (Set.add(num)) ==> num을 tuple에 저장

    과 같은식으로 할 수 있을 것 같다. (Set.add는 중복아닐 때 true 반환). 물론 이 문제에서는 중복인 경우가 주어지지

    않는다고 했으니 중복검사를 안 하는 List.add가 조금이나마 더 효율적이지 않을까....

 

 

<오늘 배운 점>

1. 문제를 똑바로 읽자 제발

2. 문제를 읽었으면 예시를 떠올려보자. 최대한 내가 생각한 풀이가 틀리게 되는 방향으로 생각해보기

3. toString() 메서드에 대한 이해......(왜 그냥 String으로 반환해주지 않는지 이해해서 유익하긴 했다.)

 

https://school.programmers.co.kr/learn/courses/30/lessons/72412

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

lv.1 어지간한 문제를 다 풀고 lv2로 넘어왔다.

lv.2의 가장 어려운문제들은 어떤지 궁금해서 마지막페이지로 가서 아무거나 누른 문제.

2021 카카오 코테 문제였나보다.

구현도 쉽지 않았지만, 계속 효율성테스트를 실패해서 약 4시간을 고민한 결과 문제를 잘못읽었음을 깨달았다.

query에 들어오는 조건이 순서가 뒤죽박죽일 수  있는줄 알았는데, 순서가 정해져있었다.

결론 : 문제가 길어도 똑바로 읽고 시작하자

 

<두번째 try>

기존 코드 문제 : 쿼리가 어떤 순서로 들어오는지 모른다고 생각을 해서, 쿼리를 split으로 나누고, 각 조건마다 for문을 돌면서 info가 그 조건을 contain하는지 확인했다. 또한, 이 작업을 info의 모든 element에 대해 해야해서 시간이 굉장히 많이 걸렸다.

추가적으로, 문제에 info는 최대 10만, query는 최대 50만의 길이로 위 코드의 이중포문은 최대 50억번을 돌게된다.😂

 

개선방안 : 언어,직군,경력,소울푸드를 구분할 수 있는 key를 만들어서 Map의 key로 사용하고, 해당 key의 value는 List로 선언을 해서, 해당key를 가진 지원자들의 점수를 삽입해주도록 한다.

==> 이렇게 하면 Map.get(key)를 통해 조건들에 해당하는 지원자들의 점수리스트를 가져오고, reverse sort 후 indexOf로 해당 점수를 넘은 사람이 몇명인지 알아낼 수 있을 듯.

 

생각해볼 점 : info에는 모든 정보가 있지만, query에는 누락된 조건이 있을 수 있다.

예를 들어, info상의 "java backend junior pizza" 지원자는 query의 "java and - and - and - 150" 조건에 의해 검색되어야한다. 두 개의 다른 string을 어떻게 같은 조건으로 검색할것인지(Map의 key를 찾을 때 정규표현식을 쓸수있나 ?)

 

<세 번째 Try>

글쓰다가 뒤로가기 눌러서 코드 캡쳐가 사라졌다 ....

 

각 조건마다 1~9의 숫자 string을 주어 조건들의 조합으로 key를 생성하였다.

<info 인코딩>

java backend senior pizza 150 ==> encode_query와 getScore 함수에서 각각

"3579"의 key와 int 150의 숫자로 인코딩하였다.

이처럼 만들어진 (3*2*2*2) = 24개의 key와, ArrayList를 가진 Map을 선언하여

각각의 key에 해당하는 지원자들의 점수를 삽입한뒤, 추후에 있을 탐색을 위해 정렬해두었다.

keyMap :    Map< String, List<Integer>>

 

<query 인코딩>

query도 마찬가지로 인코딩해주었는데, query의 경우 조건이 주어지지 않는 항목들이 있어 정규식으로 처리해주었다.

"- and backend and - and pizza 150"와 같은 query에서 and와 pizza를 지우고, split()의 regex를 "\s+"로 주어 하나 이상의 공백을 기준으로 split하였다. 

"- and backend and - and pizza"  ==> key  : "59"  score : 150

"- and - and - and - 150"              ==> key : ""      score : 150

 

<keySet 검색>

처리된 query string의 각 문자들을 모두 포함하고 있는 key들을 검색하여, 이들 각각의 리스트에서 점수로 걸러내면 정답을 얻을 수 있다고 생각했다.

- Map.keySet()의 각 원소(key)가 contains(Regex)인지 검사한다.

- Regex = "(?=.*" + String.join(")(?=.*", qr.split("")) + ").*"   ==> key가 query의 각 char을 적어도 하나 포함하는지

 

<List 검색>

regex로 얻은 key들의 List를 각각 가져오고, 각 리스트에서 >점수컷 인 사람을 세서 answer에 누적시킨다.

 

<결과분석>

효율성 테스트를 4개 중 2개 통과했다. 이 문제에서 가장 시간이 오래 걸리는 부분은 각 query에 대해서 List를 탐색하는 부분일 것이다. 내가 한 방법대로 하면 쿼리 조건이 빈 것 없이 4개를 채워서 들어오면 탐색을 한 번만 하지만, 

- - - - 150 처럼 점수만 조건으로 들어왔을때는 최대 (3*2*2*2) = 24번의 탐색을 진행하게 된다. 이게 효율성테스트를 통과하지 못하는 직접적인 이유인 듯하다.

 

<다른방법>

조건에 빈 틈이 있어도 탐색횟수를 줄이려면, 빈 조건이 있는 조건까지 key를 생성해주면 된다. 기존 (3*2*2*2)의 key에서 조건이 빠지는 경우까지 생각하여 (4*3*3*3) = 108개의 고유  key를 생성하고, info의 조건 하나마다 포함,불포함하는 경우 총 16가지의 경우의 수에 점수를 모두 삽입해주면 된다.

문제를 보자마자 생각했던 방법인데, 당시에는 "조건 영역의 개수가 늘어나거나, 조건의 option이 한두가지만 추가되어도, 하나의 info에 대해 삽입해야할 Key의 permutation이 기하급수적으로 많아지기 때문에 리소스 측면에서 낭비가 심하다" 라고 생각했다. 내가 간과한 것은 query의 개수가 많으면 많을수록 HashTable을 만드는 것에 대한 기회비용이 작아진다는 것이다. (한 번 잘 만들어놓으면 매 query에 대해 단 한 번의 탐색만 하도록 보장해줄 수 있기 때문)

내가 풀었던 방법은 삽입의 비용이 이걸 상쇄할정도로 매우 크거나, query들의 조건이 빠짐없이 전달된다고 가정했을 때 효과적일 것이라고 생각한다.

 

<최종 풀이>

 

import java.util.*;
class Solution {
    public static int[] solution(String[] info, String[] query) {
        //initialize codeMap
        codeMap.put("cpp",'1'); codeMap.put("java",'2'); codeMap.put("python",'3');
        codeMap.put("frontend",'4'); codeMap.put("backend",'5'); codeMap.put("senior",'6');
        codeMap.put("junior",'7'); codeMap.put("chicken",'8'); codeMap.put("pizza",'9'); codeMap.put("-",'-');
        int[] answer = new int[query.length];
        //Map 생성, key permutation별로 전부 삽입해주기
        Map<String, List<Integer>> keyMap = new HashMap<>();
        for (String inf:info){
            int score = getScore(inf);
            String[] encoded = encode_info(inf);
            for (String key:encoded){
                if (!keyMap.containsKey(key)){
                    keyMap.put(key, new ArrayList<Integer>());
                }
                keyMap.get(key).add(score);
            }
        }
        ///key별로 미리 정렬해놓기
        for (String elem:keyMap.keySet()){
            Collections.sort(keyMap.get(elem));
        }
        //query를 돌면서 이분탐색 진행
        int ansidx = 0;
        for (String qr:query){
            int cut = getScore(qr);
            qr = encode_query(qr);
            if (keyMap.containsKey(qr)){
                List<Integer> lst = keyMap.get(qr);
                answer[ansidx] = countscore(lst, cut);
                ansidx++;
            }
            else{
                answer[ansidx] = 0;
                ansidx++;
            }
        }
        return answer;
    }

    public static Map<String, Character> codeMap = new HashMap<>();

    public static char stringTocode(String code){

        return codeMap.get(code);
    }

    public static String[] encode_info(String info){

        String[] tempsplit = info.replaceAll(" [0-9]+", "").split(" ");
        int idx = 0;
        String[] permutations = new String[16];
        for (int i = 0; i<2; i++){
            char[] encoded = new char[4];
            encoded[0] = (i == 1)?stringTocode(tempsplit[0]):'-';
            for(int j = 0; j<2; j++){
                encoded[1] = (j == 1)?stringTocode(tempsplit[1]):'-';
                for(int k = 0; k < 2; k++){
                    encoded[2] = (k == 1)?stringTocode(tempsplit[2]):'-';
                    for(int n = 0; n<2; n++){
                        encoded[3] = (n == 1)?stringTocode(tempsplit[3]):'-';
                        permutations[idx] = String.valueOf(encoded);
                        idx++;
                    }
                }
            }
        }
        return permutations;
    }

    public static String encode_query(String query){
        String encoded = "";
        String[] tempsplit = query.replaceAll(" [0-9]+","").split(" and ");
        for (String sp:tempsplit){
            encoded += stringTocode(sp);
        }
        return encoded;
    }

    public static int getScore(String info){
        return Integer.parseInt(info.replaceAll("[^0-9*]",""));
    }

   public static int countscore(List<Integer> arr, int score){

    int mid=0;
    int end = arr.size();
    int start = 0;

    while (end > start) // end가 start보다 같거나 작아지면, 그 값이 Lower Bound이므로 반복을 종료한다.
    {
        mid = (start + end) / 2; 
            if (arr.get(mid) >= score) // 중간값이 원하는 값보다 크거나 같을 경우, 끝값을 중간값으로 설정하여 다시 탐색한다.
            end = mid;
        else start = mid + 1; // 중간값이 원하는 값보다 작을 경우, 시작값을 중간값+1로 설정하여 다시 탐색한다.
    }
    return arr.size()-start;

    }
}

조건들을 숫자로 mapping해줄 필요는 없지만, regex 연습할 겸 사용해봤다.

자바의 객체지향에 익숙해지면 리팩토링 해봐야겠다.

 

 

<배운 점>

1. 문제를 잘 읽어야된다

2. 풀이를 구상할 때, 연산량이 큰 메서드가 무엇인지 생각하고, 요구조건에 맞게 설계해야한다(중요)

3. 여러번 풀면서 문자열을 다르게 처리하며 정규표현식 쓰는 연습 해봄 --> 글 따로 정리

4. Map의 Entry element를 List로 하여 저장하는 아이디어

https://school.programmers.co.kr/learn/courses/30/lessons/12915

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

 

문자열들을 주어진 기준에 따라 분류하는 문제.

 

String[]을 각 String의 n번째 문자 기준으로 정렬하고, n번째문자가 같은경우 사전순으로 정렬해야한다.

 

정렬이 일어나는 과정에는 반드시 두 요소를 비교할 수 있는 기준이 있어야한다. 따라서 내가 정의한 클래스의 인스턴스들 간의 크기를 비교해줄 수 있게하려면 Comparable 인터페이스를 implement하여, 그 안의 compareTo 메서드를 Override 해주어야한다.

비교하고싶은 객체가 o 라고하자.

o1.compareTo(o2)는 o1이 작을때(왼쪽에있을때) 음수를, o1이 클 때 (오른쪽에있을때) 양수를 리턴해야한다.

String에는 이것이 구현되어있다.

하지만 String을 다른 기준으로 비교하고싶다고 해서 String.compareTo()를 Override할 순 없다. final이기때문에

따라서 이럴 경우 Arrays.sort()나 Collections.sort()에 내가 만든 Comparator를 전달하여 정렬할 수 있다.

cmp를 살펴보면, 먼저 문제에 정의된대로 n번째 인덱스의 character를 비교하고 있다. character는 기본형 변수로 빼기 연산이 가능하고, o1ch가 o2ch보다 앞에와야하면 음수가, 뒤에와야하면 양수가 return된다.

o1ch와 o2ch가 같을 경우, 사전 기준으로 비교해야하기 때문에 String의 compareTo를 사용하여 int를 반환하게 하였다.

 

이 문제에 정의된 방법 상으로 s1이 s2보다 좌측에 와야하면 음수, 우측에 와야하면 양수를 리턴하는 comparator를 전달하여 정렬하였다.

Comparator<type>의 타입은 기본형일 수 없다. 래퍼클래스 이용할것

 

+ Recent posts