본문 바로가기
Algorithm/Programmers Java

[JAVA] 프로그래머스 LEVEL2 [3차] 파일명 정렬

by 제우제우 2024. 10. 10.

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

 

프로그래머스

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

programmers.co.kr

코딩테스트 연습 > 2018 KAKAO BLIND RECRUITMENT > [3차] 파일명 정렬

 

난이도: LEVEL2

알고리즘 유형: 구현 + 정렬 

문제 분석 

먼저 맨 처음 String 으로 받는 파일명들을 정렬 조건에 맞게 파싱 한다.

그리고 파싱한 결과를 나는 File 클래스에 담았다. 

 

File 클래스 

static class File{
    String original; // 원본 파일 이름 
    String head; // 원본 파일에서 head 부분 
    int number;  // 원본 파일에서 숫자 부분 -> 숫자 최대 길이는 5글자 
    int index; // 입력에 주어진 순서
    public String getOriginal(){ // 메소드 래퍼런스 활용을 위해 
        return this.original;
    }
}

 

파싱 로직 

IntStream.range(0, size) 0 ~ size - 1 범위 

먼저 original 즉 본래의 파일명을 저장한다. 나중에 반환하기 위해서 

3번째 정렬 조건인 원래의 순서를 알기 위해서 index에 숫서를 저장한다. 

그리고 숫자가 나올 때까지 for문을 돌리고 숫자가 나오면 멈춘다. 멈춘 index가 처음 등장하는 숫자이니 
그전까지(0 ~ 이전 index)는 HEAD에 속한다. 

String 클래스 API인 substring를 활용하여 HEAD를 뽑는다. 

뽑은 HEAD는 File 클래스 head에 저장 
그리고 해당 index를 cnt 변수에 저장 

이제 숫자 부분을 뽑기 위해서 다시 cnt 부터 for문을 돌린다. 

숫자의 최대 크기는 5이다. 그래서 크기가 5가 넘어가거나 현재 인덱스가 숫자가 아니면 멈추고 

다시 substring으로 숫자(아직 string)를 뽑고 숫자로 변환해 준다.

Integer.parseInt() API는 0011 이런 형태의 문자열을 쉽게 0을 무시하고 11로 바꿔준다.   

 

문제를 잘 읽어보면 파일명은 HEAD - NUMBER - TAIL 이렇게 3부분으로 나눠지지만 TAIL부분이 없을 수도 있다는 조건이 있다. 

그럼 NUMBER의 크기가 5가 넘어가기 전에 문자열 전체 탐색이 끝나는 경우가 있다는 말이다.

그래서 만약 전체 탐색이 끝났는데도 아직 NUMBER 탐색의 끝나는 조건에 해당 안 한다면 HEAD를 제외하고 모든 부분이  NUMBER이니 parseInt() API를 사용해서 숫자로 바꾼다. 

 

그리고 해당 값을 바로 File 클래스 number 변수에 넣어준다.

 

이렇게 하고 File 타입의 배열인 list에 저장하면 파싱은 끝이 난다. 

int size = files.length;
File [] list = new File [size];
IntStream.range(0, size).
forEach(n -> { 
    File file = new File();
    file.index = n;
    file.original = files[n];
    int cnt = 0;
    for(int i = 0; i < files[n].length(); i++){
        char cur = files[n].charAt(i);
        if('0' <= cur && cur <= '9'){
            cnt = i;
            break;
        }
    }
    String head = files[n].substring(0, cnt).toUpperCase();
    file.head = head;
    for(int i = cnt; i < files[n].length(); i++){
        char cur = files[n].charAt(i);
        if(i == cnt + 5 || !('0' <= cur && cur <= '9')){
            file.number = Integer.parseInt(files[n].substring(cnt, i));
            break;
        }

        if(i == files[n].length() - 1){ // 마지막이면 
            file.number = Integer.parseInt(files[n].substring(cnt, i + 1));
        }
    }
    list[n] = file;
});

 

정렬 로직 

먼저 File 타입의 배열 list를 stream 데이터로 바꿔준다.

그런다음 sorted API를 사용하고 안에 Comparator를 람다식 표현으로 구현한다. 

정렬은 간단하다.

만약 HEAD가 서로 다르면 head의 사전 순서에 빠른 게 먼저 오니까 compareTo 메소드를 활용한다.
String은 Comparable 인터페이스를 구현해서 compareTo를 통해서 정렬이 가능하다. 

 

만약 HEAD가 같다면 숫자의 오름차순으로 정렬한다. 

숫자도 같다면 원래 처음의 순서를 유지 시켜준다. 

이렇게 정렬이 끝나면 map을 사용해서 original (전체 파일명)만 가져온다. 

 

이때 메소드 래퍼런스를 활용하기 위해 미리 File 클래스에 getter를 만들어 두었다. 

그리고 toArray API를 활용하여 String 배열로 반환하면 전체 로직이 끝난다. 

toArray API 또한 String 배열 생성자 메소드 래퍼런스를 활용하였다. 

return Arrays.stream(list).sorted((o1, o2) -> {
        // 정렬 정의 
        if(!o1.head.equals(o2.head)) return o1.head.compareTo(o2.head);
        if(o1.number != o2.number) return o1.number - o2.number;
        return o1.index - o2.index;
    }).map(Solution.File::getOriginal).toArray(String[]::new);

 

정답 코드 

import java.util.*;
import java.util.stream.*;
class Solution {
    static class File{
        String original; // 원본 파일 이름 
        String head; // 원본 파일에서 head 부분 
        int number;  // 원본 파일에서 숫자 부분 -> 숫자 최대 길이는 5글자 
        int index; // 입력에 주어진 순서
        public String getOriginal(){ // 메소드 래퍼런스 활용을 위해 
            return this.original;
        }
    }
    public String[] solution(String[] files) {
        int size = files.length;
        File [] list = new File [size];
        IntStream.range(0, size).
            forEach(n -> { 
                File file = new File();
                file.index = n;
                file.original = files[n];
                int cnt = 0;
                for(int i = 0; i < files[n].length(); i++){
                    char cur = files[n].charAt(i);
                    if('0' <= cur && cur <= '9'){
                        cnt = i;
                        break;
                    }
                }
                String head = files[n].substring(0, cnt).toUpperCase();
                file.head = head;
                for(int i = cnt; i < files[n].length(); i++){
                    char cur = files[n].charAt(i);
                    if(i == cnt + 5 || !('0' <= cur && cur <= '9')){
                        file.number = Integer.parseInt(files[n].substring(cnt, i));
                        break;
                    }
                    if(i == files[n].length() - 1){ // 마지막이면 
                        file.number = Integer.parseInt(files[n].substring(cnt, i + 1));
                    }
                }
                list[n] = file;
            });
        
        return Arrays.stream(list).sorted((o1, o2) -> {
            if(!o1.head.equals(o2.head)) return o1.head.compareTo(o2.head);
            if(o1.number != o2.number) return o1.number - o2.number;
            return o1.index - o2.index;
        }).map(Solution.File::getOriginal).toArray(String[]::new);
        
    }
}