필요한 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))
부분을 한 줄 위로 올렸어도 해결 가능한 부분이였다.
블로그도 하겠다 이제는 정말 정규식을 해야할 때가 온 것이다. (이렇게나 비통할 수가 없다.)
정규표현식에 대해 공부한 내용은 이쪽으로 올리기로 하고, 우선은 해결 내용은 다음과 같다.
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년 네이버 코테에서 정렬을 제대로 공부하지 않아 떨어지는 불상사를 겪었으니, 아무래도 반드시 잘 알아놓을 필요가 있다.
정렬과 관련해서도 기회가 되면 한 번 정리야겠다.