문제 설명
데이터 처리 전문가가 되고 싶은 "어피치"는 문자열을 압축하는 방법에 대해 공부를 하고 있습니다. 최근에 대량의 데이터 처리를 위한 간단한 비손실 압축 방법에 대해 공부를 하고 있는데, 문자열에서 같은 값이 연속해서 나타나는 것을 그 문자의 개수와 반복되는 값으로 표현하여 더 짧은 문자열로 줄여서 표현하는 알고리즘을 공부하고 있습니다.
간단한 예로 "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는 알파벳 소문자로만 이루어져 있습니다.
입출력 예
sresult
"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):
#문자열을 나눠야함.
#s에 칸마다 공백을 추가시켜서 split()으로 리스트를 나눌 수 있게하자
#cnt 가 문자열 자르는 단위임 범위는 1~len(s)
cnt = 1
L = int(len(s)/2)+1#문자열 절반이 넘어가게 나누면 어차피 의미 없음.
length=[]*L # 압축 후 문자길이
div = 1 # 기준, 몇글자씩 나눌거냐
while(div != L+1):
string =''
for i in range(0,len(s),div):
string += " " + s[i:i+div]
string += " " #for문이 마지막에 작동 안해서 공백을 추가시킴
lst = string.split(" ") #공백으로 나눠서 list화
lst.pop(0)
comp = []
for j in range(len(lst)-1):
if lst[j] == lst[j+1]:
cnt +=1
else:
if cnt == 1:
comp.append(lst[j])
continue
else:
comp.append(str(cnt) + lst[j])
cnt = 1
if j == len(lst) - 1:
comp.append(str(cnt) + lst[j])
cnt = 1
compress= ''.join(comp)
length.append(len(compress))
div += 1
return min(length)
음 다른 사람풀이는
더 잘풀었다.
내가 주석을 따로 달았다.
def compress(text, tok_len): #문자열 압축("압축할 문자열", "글자 수 기준")
words = [text[i:i+tok_len] for i in range(0, len(text), tok_len)]# 글자수 기준으로 list에 문자열을 집어넣음
# words 부분이 정말 잘 짜여져서 배울만하다
res = []
cur_word = words[0] #현재 조사하고 있는 단어
cur_cnt = 1 # 초기값
#현재 글자수 기준으로 나누어진 리스트와 맨 처음 단어를 제외하고 마지막에 빈 문자열을 추가시킨 것을 짝지음
# ['a','a','a','c']라 치면
# ['a','a','c','']하고 짝짓는 것이다.
#즉 zip으로 나온 결과물을 ('a','a'),('a','a'),('a','c'),(c,'') 이렇게 나오겠고
#a,b는 각 요소 가 되겠다.
for a, b in zip(words, words[1:] + ['']):
#이미 맨처음 것은 세었으니 같다면 +
if a == b:
cur_cnt += 1
#다르다면 이제 res라는 빈 문자열에 해당 문자와, 반복횟수 추가
# 다른 순간에는 당연히 b가 달라서 그런 것이고
# b를 현재단어로 바꾸고 count 다시 1부터 시작
else:
res.append([cur_word, cur_cnt])
cur_word = b
cur_cnt = 1
#끝나면 다 세어져있음.
#res에 집어넣은 word,와 cnt를 꺼내어
#cnt가 word의 길이 + cnt가 1보다 클때만 거내어 길이에 포함시켜 더한다.
return sum(len(word) + (len(str(cnt)) if cnt > 1 else 0) for word, cnt in res)
def solution(text):
# 이풀이도 절반 이상의 기준으로 나누는 것은 하지 않음
return min(compress(text, tok_len) for tok_len in list(range(1, int(len(text)/2) + 1)) + [len(text)])
사실 여기서 중요한 것은
내가 생각하기엔
words이다.
그것만 충분히 했다면 다른 사람도 얼마든지 했을 듯 하다.
zip을 이용하는 것까지 생각하기는 조금 어려웠을 수 있다.
하지만 zip을 이용하는 것이 index로 비교하는 것보다 훨씬 쉬웠다.
생각만 할 수 있다면
'문제풀이(Problem Solving)' 카테고리의 다른 글
피보나치 수, Python3 [프로그래머스] (0) | 2021.03.18 |
---|---|
H-index, Python3 [ 프로그래머스] (0) | 2021.03.18 |
소수 찾기-lv2, Python3 [프로그래머스] (0) | 2021.03.17 |
더 맵게, Python3 시간초과,원리 [프로그래머스] (0) | 2021.03.16 |
가장 큰 수, Python3 원리 [프로그래머스] (0) | 2021.03.16 |