[프로그래머스] 파일명 정렬

Hyeonjun·2022년 8월 8일
1

문제

파일명 정렬

솔루션

1. 문자열 단위로 나눠서 해결하자.

필요한 HEAD와 NUMBER만 확인해서 넘어갈 수 있겠다 생각했다.
TAIL은 정렬에 필요하지 않으니 넘어가고, HEAD비교 후 NUMBER비교하여 정렬하면 될 것이라 생각했다.

import java.util.*;

class Solution {
    public String[] solution(String[] files) {
        
        Arrays.sort(files, (o1, o2) -> {
            FileInfo headerO1 = getHeader(o1);
            FileInfo headerO2 = getHeader(o2);
            int numberO1 = getNumber(o1, headerO1.index).index;
            int numberO2 = getNumber(o2, headerO2.index).index;
            
            if(headerO1.value.equals(headerO2.value)) {
                 return numberO1 - numberO2;    
            }
            return headerO1.value.compareTo(headerO2.value);
        });
        
        return files;
    }
    
    // HEAD 가져오기
    private FileInfo getHeader(String file) {
        StringBuilder sb = new StringBuilder();
        int index = 0;
        for(int i = 0; i < file.length() - 1; i++) {
            index = i;
            if(file.charAt(i + 1) <= '9' && file.charAt(i + 1) >= '0') break;
            sb.append(file.charAt(i));
        }
        return new FileInfo(sb.toString().toLowerCase(), index + 1);
    }
    
    // NUMBER 가져오기
    private FileInfo getNumber(String file, int index) {
        StringBuilder sb = new StringBuilder();
        for(int i = index; i < file.length(); i++) {
            if(file.charAt(i) > '9' || file.charAt(i) < '0') break;
            sb.append(file.charAt(i));
        }
        if(sb.toString().equals("")) {
            return new FileInfo(null, 1);
        }
        
        // Number를 가져올 땐, index에 값을 가져올 수 있도록 구현했다.
        return new FileInfo(null, Integer.parseInt(sb.toString()));
    }
    
    // FILE 형태로 가져옴.
    private class FileInfo {
        String value;
        int index; // Number를 가져오기 위해 index를 설정하여 다음 index로 불러올 수 있도록
        
        FileInfo(String value, int index) {
            this.value = value;
            this.index = index;
        }
    }
}

문제점이 있다.
해당 방법으로 문제를 해결할 때 실패하는 경우가 있다는 것.

import java.util.*;

class Solution {
    public String[] solution(String[] files) {
        
        Arrays.sort(files, (o1, o2) -> {
            FileInfo headerO1 = getHeader(o1);
            FileInfo headerO2 = getHeader(o2);
            int numberO1 = getNumber(o1, headerO1.index).index;
            int numberO2 = getNumber(o2, headerO2.index).index;
            
            if(headerO1.value.equals(headerO2.value)) {
                 return numberO1 - numberO2;
            }
            return headerO1.value.compareTo(headerO2.value);
        });
        
        return files;
    }
    
    private FileInfo getHeader(String file) {
        StringBuilder sb = new StringBuilder();
        int index = 0;
        for(int i = 0; i < file.length(); i++) {
            index = i;
            if(file.charAt(i) <= '9' && file.charAt(i) >= '0') break;
            sb.append(file.charAt(i));
        }
        return new FileInfo(sb.toString().toLowerCase(), index);
    }
    
    private FileInfo getNumber(String file, int index) {
        StringBuilder sb = new StringBuilder();
        for(int i = index; i < file.length(); i++) {
            if(file.charAt(i) > '9' || file.charAt(i) < '0') break;
            sb.append(file.charAt(i));
        }
        if(sb.toString().equals("")) {
            return new FileInfo(null, 1);
        }
        
        return new FileInfo(null, Integer.parseInt(sb.toString()));
    }
    
    private class FileInfo {
        String value;
        int index;
        
        FileInfo(String value, int index) {
            this.value = value;
            this.index = index;
        }
    }
}

개선된 사항은 HEAD를 가져올 때 i+1에서 확인하고 만약 해당 부분이 숫자의 영역이라면 넘어가도록 설계한 부분을 i번째에서 바로 확인할 수 있도록 했다.
기존 코드에서는 다음 문자열을 확인한 후에 숫자라면 그냥 넘어가면서 제일 마지막 문자를 가져가지 못했다.
예컨데 img12.jpg가 있었다면 HEAD = im와 같은 형태로 넘어간 것.
해당 부분을 확인하고 범위를 바꿔서 해결했지만, sb.append(file.charAt(i))부분을 한 줄 위로 올렸어도 해결 가능한 부분이였다.

2. 정규식

블로그도 하겠다 이제는 정말 정규식을 해야할 때가 온 것이다. (이렇게나 비통할 수가 없다.)
정규표현식에 대해 공부한 내용은 이쪽으로 올리기로 하고, 우선은 해결 내용은 다음과 같다.

public String[] solution(String[] files) {
        Pattern p = Pattern.compile("([a-z\\s.-]+)([0-9]{1,5})");

        Arrays.sort(files, (o1, o2) -> {
            Matcher m1 = p.matcher(o1.toLowerCase());
            Matcher m2 = p.matcher(o2.toLowerCase());
            m1.find();
            m2.find();

            if(m1.group(1).equals(m2.group(1))) {
                return Integer.parseInt(m1.group(2)) - Integer.parseInt(m2.group(2));
            }
            return m1.group(1).compareTo(m2.group(1));
        });

        return files;
    }

정규식 부분만 잘라서 보자면,
1. ([a-z\\s.-]+)
group의 첫번째 부분이다.
a-z로 알파벳 집합 전체를, 처음 나오는 \로 확장 문자임을 알리고, \s로 공백문자를, .-로 .와 -를 포함한다. 이 문자들이 1번 이상 나올 수 있으므로 마지막에 +를 붙인다.
이를 통해 첫번째 그룹에서 HEAD를 모두 포함할 수 있다.
2. ([0-9]{1,5})
group의 두번째, NUMBER부분이다.
[0-9]로 숫자만을 확인할 수 있으며, {1,5}를 통해 해당 문자열의 길이가 1~5개가 될 수 있음을 보여준다.

이렇게 정규식을 처리하고 Mather를 통해 문자열을 가져오며, 비교하면 이전 해결 방법과는 확연히 눈에 띄는 빠른 해결을 보여줄 수 있다.

마치며

정규식은 2~3년 전까지도 카카오 블라인트 테스트 문제로 자주 나왔던 것 같다. 최근에는 그렇게 많이 보이진 않는 것 같지만, 1번 문제는 언제나 정규식이 나올 수 있으니 주의가 필요하긴 하다.
정규식 뿐만 아니라 정렬도 분명 필요한 문제인데, 22년 네이버 코테에서 정렬을 제대로 공부하지 않아 떨어지는 불상사를 겪었으니, 아무래도 반드시 잘 알아놓을 필요가 있다.
정렬과 관련해서도 기회가 되면 한 번 정리야겠다.

profile
더 나은 성취

0개의 댓글