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

hyun·2022년 9월 18일
3

알고리즘 문제

목록 보기
10/10

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

문제 이해

주어진 파일명을 규칙에 맞게 정렬하는 문제이다.
숫자를 기준으로 파일명을 head, number, tail로 구분하는데, head는 문자로만 구성되어 있으며 문자열 시작~숫자가 나올 때까지가 head이다. number는 숫자로만 이루어져 있다. 붙어있는 숫자들인 number 이후의 문자열이 tail이다. tail은 숫자, 문자 상관없이 number 이후부터가 모두 tail이다.

다음의 우선순위에 따라 리스트를 정렬해야 한다.

  • 1단계: head를 기준으로 사전 순 정렬 (대소문자 무시)
  • 2단계: 1단계 통과 시 숫자 정렬 (앞에 붙은 0 무시)
  • 3단계: 2단계 통과 시 원래 순서 유지

문제 접근

문제 풀이의 핵심 로직은 [1] head/number/tail을 적절히 파싱 후, [2] 단계에 맞춰 정렬 및 출력하는 것이다.

대소문자 구분을 하지 않으므로 lower()를 써서 비교대상을 소문자로 통일하고, sorted()lambda를 사용해 단계별 기준을 적용해 정렬하면 된다. 2단계에서 앞에 붙은 0을 무시하려면, 그러니까 001과 01을 같은 1로 취급하려면 int() 를 활용하면 되겠다.

문자열 파싱

정규식 사용

우선 입력으로 주어진 리스트를 숫자를 기점으로 분해하라는 조건에서 파이썬 정규식의 re.split()을 떠올렸다.

정규식 미사용

반복문을 돌면서 isdigit()을 사용, 숫자가 나오면 그 이전까지가 head이다. head와 number를 구분하는 것은 쉬운데, number와 tail을 어떻게 구분할까 고민했다. 처음엔 원래 순서를 기억하는 배열을 만들어 인덱스를 부여하려고 했는데, 파이썬에서 제공하는 sort() 함수는 stable을 지원하는 함수라는 걸 알게 되어 그냥 head와 number만 고려하면 문제가 해결되었다.

stable sort란?

정렬하기 전과 후의 순서를 보장해주는 sort 방법이다. 예를 들면, [5, 1, 3, 2, 2, 4]를 정렬한다고 했을 때 결과는 당연히 [1, 2, 2, 3, 4, 5]가 될 것이다. 여기서 stable한 sort를 했다면 결과는 [1, 2, 2, 3, 4, 5]가 될 것이다. 볼드 처리한 2가 sort 전/후에도 여전히 뒤의 순서를 유지하는 것이다.

예제

from operator import itemgetter
data = [('앞',3), ('x',2), ('뒤',3), ('x',1)]
ret = sorted(data, key=itemgetter(1)) # 튜플 뒤의 숫자를 기준으로 정렬
print(ret) # [('x', 1), ('x', 2), ('앞', 3), ('뒤', 3)]

뒤의 숫자를 기준으로 정렬했다. 1,2,3,3 순서로 정렬이 될 것이다. 같은 3값을 가지고 있는 ('앞',3), ('뒤',3)는 순서가 그대로 유지된다.

풀이

정규식 풀이

import re
def solution(files):
   temp = [re.split(r"([0-9]+)", s) for s in files]
   sort = sorted(temp, key = lambda x: (x[0].lower(), int(x[1])))
   return [''.join(s) for s in sort]

파이썬의 r prefix

temp = [re.split(r"([0-9]+)", s) for s in files]

이 라인의 re.split() 정규식 앞에 r이 붙어있는데, 이건 raw를 의미한다. 아래의 예시코드를 보면 직관적으로 이해가 된다.

print("hello\n") # hello와 줄바꿈이 출력
print(r"hello\n") # raw string, \n이 문자열로 취급되어 출력

정규식 미사용 풀이

def solution(files):
    temp = []
    head, number, tail = '', '', ''
    for file in files:       
        for i in range(len(file)):
        	# head와 number 구분
            if file[i].isdigit():
                head = file[:i]
                number = file[i:]
                
                for j in range(len(number)):
                	# number와 tail 구분
                    if not number[j].isdigit():
                        tail = number[j:]
                        number = number[:j]
                        break
                        
                temp.append((head, number, tail))
                head, number, tail = '', '', ''
                break

    temp = sorted(temp, key=lambda x:(x[0].lower(), int(x[1])))
    return [''.join(i) for i in temp]
profile
프론트엔드를 공부하고 있습니다.

0개의 댓글