⚙️ 알고리즘/문제풀이

[Python] 프로그래머스 파일명 정렬, 프로그래머스 H-Index

dev_zoe 2025. 5. 7. 12:32
반응형

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

- 먼저, 배열 sort시 커스텀하게 비교해야하므로 cmp_to_key 라이브러리를 사용했고, 비교하는 함수인 compare 함수를 작성했다.

- 정렬의 조건을 정리하자면 다음과 같다.

1) HEAD: 문자로 이루어져있으며, 반드시 한글자 이상이다. 파일명은 HEAD 기준으로 사전순으로 정렬하며, 대소문자를 구분하지 않는다.

2) NUMBER: 최소 한글자 이상의 연속된 숫자로 이루어져 있으며, 숫자 순으로 정렬한다.

3) TAIL: HEAD와 NUMBER 외의 나머지 부분으로, 이부분은 정렬에 영향을 주지 않는다.

 

❌ 틀린 풀이

from functools import cmp_to_key

def compare(a, b):
    axis = 1  # 1이면 뒤, 0이면 그대로, -1이면 앞
    head_end_index = 0
    number_end_index = 0

    for i in range(len(a)):
        if a[i].isdigit():
            head_end_index = i  # 숫자가 시작되는 첫 인덱스여야하므로 인덱스를 찾고 break한다.
            break

    for i in range(head_end_index, len(a)):
        if not a[i].isdigit():   # 위 head_end_index에서부터 숫자가 아니게 되는 첫 지점을 찾게 되면, 그 사이가 숫자가 된다.
            number_end_index = i
            break
    else:
        number_end_index = len(a)

    a_header = a[:head_end_index]
    b_header = b[:head_end_index]
    a_number = a[head_end_index:number_end_index]
    b_number = b[head_end_index:number_end_index]

    if a_header.lower() > b_header.lower():
        axis = 1
    elif a_header.lower() < b_header.lower():
        axis = -1
    else:
        if int(a_number) > int(b_number):
            axis = 1
        elif int(a_number) < int(b_number):
            axis = -1
        else:
            axis = 0

    return axis

def solution(files):
    return sorted(files, key=cmp_to_key(compare))

 

해당 코드의 문제는 다음과 같다.

1) HEAD와 NUMBER, TAIL을 분리하는 기준을 무조건 a로 둔점

--> a와 b의 HEAD, NUMBER, TAIL 을 분리하는 인덱스는 서로 다를 수 있다.

ex) "F-5 Freedom" 과 "B-50 Superfortress"에서 HEAD는 각각 F-, B- / NUMBER는 5와 50으로, NUMBER와 TAIL을 구분하는 인덱스는 서로 다르다.

--> 따라서 a와 b를 각각 HEAD, NUMBER, TAIL로 분리 후 판단해야한다.

 

2) 

    number_end_index = 0
    
    for i in range(head_end_index, len(a)):
        if not a[i].isdigit():   # 위 head_end_index에서부터 숫자가 아니게 되는 첫 지점을 찾게 되면, 그 사이가 숫자가 된다.
            number_end_index = i
            break

 

해당 코드는 TAIL의 "아무 글자도 없을 수도 있다" 라는 조건을 무시한 것이다.

즉, 예를들어 파일명이 "F-15"일 경우 HEAD는 F-, NUMBER는 15, TAIL은 빈 문자열이므로 이 경우에 number_end_index가 0이 되어 하단에 header, number를 분리하는 변수에서 런타임 오류가 발생하게 된다.

--> 따라서 number_end_index를 file의 끝 인덱스로 둔 다음 반복문을 실행함으로써 TAIL이 빈 문자열일 경우를 커버한다.

 

✅ 수정한 풀이

from functools import cmp_to_key

def compare(a, b):
    axis = 0          # 1이면 뒤, 0이면 그대로, -1이면 앞으로 정렬하기 이므로, 해당 값을 반환하기 위한 축
    
    def extract_from_file(file):   # 함수를 별도로 생성하여 a, b 따로 분리해서 비교하도록 함

        # 1) HEAD 분리 작업
        head_end_index = 1    # 최소한 1글자 이상이므로 초기값 1
        
        for i in range(len(file)):
            if file[i].isdigit():
                head_end_index = i
                break
                
        # 2) NUMBER 분리 작업
        
        number_end_index = len(file)    # 위 틀린 코드에서 수정한 부분

        for i in range(head_end_index, len(file)):
            if not file[i].isdigit():   # number가 시작하는 지점부터 끝까지의 범위에서 처음으로 숫자가 아닌 수가 나타나는 구간이 number임
                number_end_index = i
                break
                
        return file[:head_end_index], file[head_end_index:number_end_index]
    
    a_header, a_number = extract_from_file(a)
    b_header, b_number = extract_from_file(b)

	# 대소문자 구분 없음 --> 소문자화한 다음에 비교
    # 사전순 정렬
    if a_header.lower() > b_header.lower():
        axis = 1
    elif a_header.lower() < b_header.lower():
        axis = -1
    else:    # 만약에 HEAD가 서로 같다면, NUMBER를 숫자 순으로 정렬
        if int(a_number) > int(b_number):
            axis = 1
        elif int(a_number) < int(b_number):
            axis = -1
        else:    # HEAD와 NUMBER도 서로 같다면, 주어진 순서를 유지 --> 0
            axis = 0

    return axis

def solution(files):
    return sorted(files, key=cmp_to_key(compare))

 

2. 문제: https://school.programmers.co.kr/learn/courses/30/lessons/42747

- 나는 문제 설명보고 야생(?)으로 풀었는데, 카테고리가 정렬이다 ..!!!

 

✅ 나의 풀이

def solution(citations):
    answer = 0 #h
    cited = 0
    notcited = 0
    arr = []

    while answer <= max(citations):  # 0부터 인용의 최대횟수까지 차례대로 비교함
        cited = 0
        notcited = 0

        for i in citations:
            if i>=answer:
                cited += 1
            if i<=answer:
                notcited += 1

        if cited>=answer and notcited<=answer:
            arr.append(answer)
        answer += 1

    return max(arr) if len(arr) else 0

 

✅ 다른 사람 풀이

def solution(citations):
    citations = sorted(citations)
    l = len(citations)
    for i in range(l):
        if citations[i] >= l-i:
            return l-i
    return 0

 

- 첫번째 입출력에 대해 정렬하게 되면 [0, 1, 3, 5, 6] 이다. 이에 대해 따져보면

0: 0번 이상 인용된 논문이 5편 이상, 나머지 논문 1번 이하 인용

1: 1번 이상 인용된 논문이 4편 이상, 나머지 논문 2번 이하 인용

3: 3번 이상 인용된 논문이 3편 이상, 나머지 논문 3번 이하 인용

5: 5번 이상 인용된 논문이 2편 이상, 나머지 논문 4번 이하 인용

6: 6번 이상 인용된 논문이 1편 이상, 나머지 논문 5번 이하 인용

- citations[i]: 논문이 인용된 최소 횟수, len - i: citations[i]번 이상 인용된 논문의 갯수

 

오늘 알게 된 점

1. 문자열 관련 python 함수 다시 정리! 꼭 기억하자

 

isdigit() : 해당 character가 정수인지에 대한 bool 값

isalpha() : 해당 character가 알파벳 문자인지에 대한 bool 값 (특수문자의 경우 알파벳이 아니기때문에 False 반환함)

isupper() : 해당 character가 대문자인지에 대한 bool 값

islower() : 해당 character가 소문자인지에 대한 bool 값

 

문자열 전체 대문자로 치환하여 문자열 반환 : upper()

문자열 전체 소문자로 치환하여 문자열 반환 : lower()

문자열 처음 대문자로 시작하도록 변환하여 문자열 반환 : capitalize()

문자열 전체 소<->대문자로 치환하여 문자열 반환 : swapcase()

 

 

반응형