Notice
Recent Posts
Recent Comments
Link
«   2025/02   »
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28
Tags
more
Archives
Today
Total
관리 메뉴

개발자입니다

[프로그래머스] 문자열 압축 본문

코딩테스트/Python 문제: 구현·시뮬레이션·완전탐색

[프로그래머스] 문자열 압축

끈기JK 2025. 2. 10. 22:11

문제 설명

데이터 처리 전문가가 되고 싶은 **"어피치"**는 문자열을 압축하는 방법에 대해 공부를 하고 있습니다. 최근에 대량의 데이터 처리를 위한 간단한 비손실 압축 방법에 대해 공부를 하고 있는데, 문자열에서 같은 값이 연속해서 나타나는 것을 그 문자의 개수와 반복되는 값으로 표현하여 더 짧은 문자열로 줄여서 표현하는 알고리즘을 공부하고 있습니다.

간단한 예로 "aabbaccc"의 경우 "2a2ba3c"(문자가 반복되지 않아 한번만 나타난 경우 1은 생략함)와 같이 표현할 수 있는데, 이러한 방식은 반복되는 문자가 적은 경우 압축률이 낮다는 단점이 있습니다. 예를 들면, "abcabcdede"와 같은 문자열은 전혀 압축되지 않습니다. "어피치"는 이러한 단점을 해결하기 위해 문자열을 1개 이상의 단위로 잘라서 압축하여 더 짧은 문자열로 표현할 수 있는지 방법을 찾아보려고 합니다.

예를 들어, "ababcdcdababcdcd"의 경우 문자를 1개 단위로 자르면 전혀 압축되지 않지만, 2개 단위로 잘라서 압축한다면 "2ab2cd2ab2cd"로 표현할 수 있습니다. 다른 방법으로 8개 단위로 잘라서 압축한다면 "2ababcdcd"로 표현할 수 있으며, 이때가 가장 짧게 압축하여 표현할 수 있는 방법입니다.

다른 예로, "abcabcdede"와 같은 경우, 문자를 2개 단위로 잘라서 압축하면 "abcabc2de"가 되지만, 3개 단위로 자른다면 "2abcdede"가 되어 3개 단위가 가장 짧은 압축 방법이 됩니다. 이때 3개 단위로 자르고 마지막에 남는 문자열은 그대로 붙여주면 됩니다.

압축할 문자열 s가 매개변수로 주어질 때, 위에 설명한 방법으로 1개 이상 단위로 문자열을 잘라 압축하여 표현한 문자열 중 가장 짧은 것의 길이를 return 하도록 solution 함수를 완성해주세요.

제한사항

  • s의 길이는 1 이상 1,000 이하입니다.
  • s는 알파벳 소문자로만 이루어져 있습니다.

입출력 예

s result
"aabbaccc" 7
"ababcdcdababcdcd" 9
"abcabcdede" 8
"abcabcabcabcdededededede" 14
"xababcdcdababcdcd" 17

입출력 예에 대한 설명

입출력 예 #1

문자열을 1개 단위로 잘라 압축했을 때 가장 짧습니다.

입출력 예 #2

문자열을 8개 단위로 잘라 압축했을 때 가장 짧습니다.

입출력 예 #3

문자열을 3개 단위로 잘라 압축했을 때 가장 짧습니다.

입출력 예 #4

문자열을 2개 단위로 자르면 "abcabcabcabc6de" 가 됩니다.

문자열을 3개 단위로 자르면 "4abcdededededede" 가 됩니다.

문자열을 4개 단위로 자르면 "abcabcabcabc3dede" 가 됩니다.

문자열을 6개 단위로 자를 경우 "2abcabc2dedede"가 되며, 이때의 길이가 14로 가장 짧습니다.

입출력 예 #5

문자열은 제일 앞부터 정해진 길이만큼 잘라야 합니다.

따라서 주어진 문자열을 x / ababcdcd / ababcdcd 로 자르는 것은 불가능 합니다.

이 경우 어떻게 문자열을 잘라도 압축되지 않으므로 가장 짧은 길이는 17이 됩니다.

내 코드

통과함

def solution(s):
    if len(s) <= 2:
        return len(s)

    s_len = len(s)
    s_half = int(s_len / 2)
    new_sentences = []

    for step in range(1, s_half + 1):
        char = ''
        count = 1
        new_sentence = ''

        for i in range(0, s_len, step):
            if i + (step * 2) <= s_len and s[i:i + step] == s[i + step:i + (step * 2)]:
                char = s[i:i + step]
                count += 1
            elif count > 1:
                new_sentence += str(count) + char
                count = 1
            else:
                new_sentence += s[i:i + step]
                count = 1

        new_sentences.append(new_sentence)

    min_len = 1000

    for i, e in enumerate(new_sentences):
        new_len = len(e)

        if min_len > min(min_len, new_len):
            min_len = min(min_len, new_len)

    answer = min_len

    return answer

chatGPT

코드를 최적화하고 개선할 부분을 정리하면 다음과 같습니다.

문제점 및 개선 사항

  1. 불필요한 조건문 (if i + (step * 2) <= s_len)
    • 현재 구현에서는 i + (step * 2) <= s_len을 검사하지만, 사실 s[i + step:i + (step * 2)] 비교만으로도 충분함.
  2. 반복되는 문자열 압축 로직 개선
    • char을 따로 관리하는 대신, prev라는 변수로 현재 비교할 문자열을 유지하는 것이 직관적.
    • new_sentence에 직접 문자열을 추가하는 방식보다 리스트를 사용하고 마지막에 ''.join()을 하는 것이 성능적으로 유리함.
  3. 불필요한 리스트 (new_sentences) 제거
    • 압축된 결과를 리스트에 모두 저장하고 다시 min()을 찾는 비효율적인 구조.
    • 문자열 길이 최소값을 바로 갱신하는 방식이 더 효율적임.
  4. min()을 직접 활용하여 최소 길이 찾기 최적화
    • min_len > min(min_len, new_len)과 같은 중복된 min() 호출을 없애고, min()을 한 번만 사용하도록 수정.

개선된 코드

def solution(s):
    if len(s) == 1:  # 문자열 길이가 1이면 압축할 필요 없음
        return 1

    s_len = len(s)
    min_length = s_len  # 최악의 경우 압축되지 않는 원래 길이

    for step in range(1, s_len // 2 + 1):
        compressed = []
        prev = s[:step]  # 첫 단위 문자열
        count = 1

        for i in range(step, s_len, step):
            curr = s[i:i + step]  # 현재 단위 문자열

            if curr == prev:  # 이전 문자열과 같다면 개수 증가
                count += 1
            else:
                compressed.append(f"{count}{prev}" if count > 1 else prev)  # 압축된 문자열 추가
                prev = curr  # 새로운 비교 대상 설정
                count = 1

        compressed.append(f"{count}{prev}" if count > 1 else prev)  # 마지막 문자열 처리
        min_length = min(min_length, len("".join(compressed)))  # 최소 길이 갱신

    return min_length

개선된 코드의 장점

  1. new_sentences 리스트 제거 → 한 번의 min() 비교만으로 최소 길이 찾기 가능
  2. compressed.append() 방식 사용 → + 연산 대신 리스트에 추가 후 ''.join() 활용 (속도 개선)
  3. 코드 가독성 향상 → prev와 curr를 활용한 반복 구조로 중복 로직 제거
  4. 불필요한 if 문 제거 → s[i + step:i + (step * 2)] 비교를 바로 수행

시간 복잡도 분석

  • O(N^2)이지만, 개선된 방식은 연산량을 줄여 기존 코드보다 빠르게 동작할 가능성이 큼.
  • 문자열 압축 과정에서 O(N) 연산을 수행하고, N/2개의 단위 길이를 시도하므로 최악의 경우 O(N^2)

이제 개선된 코드로 테스트하면 더욱 최적화된 결과를 얻을 수 있습니다! 🚀

부가 질문

리스트 내부 원소의 len을 구해서 가장 작은 크기인 원소를 반환하는 쉬운 방법은?

리스트 내부 원소의 len() 값을 기준으로 가장 작은 원소를 찾는 가장 쉬운 방법은 min() 함수를 key=len과 함께 사용하는 것입니다.

예제 코드

strings = ["apple", "banana", "kiwi", "grape"]
shortest = min(strings, key=len)
print(shortest)  # "kiwi"

설명

  • min(strings, key=len)
    • len() 함수를 기준으로 가장 작은 길이를 가진 문자열을 반환

응용

숫자 리스트가 아닌 문자열 리스트, 튜플 리스트, 2D 리스트에서도 사용 가능.

lists = [[1, 2, 3], [4, 5], [6, 7, 8, 9]]
shortest_list = min(lists, key=len)
print(shortest_list)  # [4, 5]

이 방식이 가장 짧고 직관적인 방법입니다! 🚀

내 생각

  1. prev, curr 를 사용해서 좀 더 직관적으로 비교하는 로직이 괜찮아 보인다.
  2. 리스트 내부 원소 중 가장 작은 항목 반환 방법도 혹시 모르니 외우자. min(strings, key=len)