안녕하세요. 김용성입니다.
이번 포스팅에서는 지난 2020년도 KAKAO BLIND RECRUITMENT 코딩테스트의 1번 문제인 문자열 압축 문제를 풀어보도록 하겠습니다.
2020 KAKAO BLIND RECRUITMENT 1번 문제 링크
문제 설명
데이터 처리 전문가가 되고 싶은 "어피치"는 문자열을 압축하는 방법에 대해 공부를 하고 있습니다. 최근에 대량의 데이터 처리를 위한 간단한 비손실 압축 방법에 대해 공부를 하고 있는데, 문자열에서 같은 값이 연속해서 나타나는 것을 그 문자의 개수와 반복되는 값으로 표현하여 더 짧은 문자열로 줄여서 표현하는 알고리즘을 공부하고 있습니다.
간단한 예로 "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이 됩니다.
어려운 문제는 아니였다고 생각해요. 단, 입출력 예 #5를 잘 보아야합니다. 저는 처음에 제일 앞부터 정해진 길이만큼 잘라야한다는 조건을 보지 못하고, 어떻게 풀면 좋을까에 대해 깊게 생각했었어요. 하지만 저 조건이 있는 한 문자열의 맨앞서부터 자르면 되기 때문에 문제의 난이도는 확 쉬워지죠.
문자열을 그냥 나누어서 배열에 넣어주면 됩니다.
단 1개씩,2개씩,3개씩 ----- 문자열 길이만큼 다 나누어서 그때 나오는 문자열들의 길이를 비교해주면돼요.
저는 이런식으로 반복문 내에 컴프리핸션을 작성하여 이를 처리해주었습니다.
for length in range(1,len(s)):
array=[s[i:i+length] for i in range(0, len(s), length)]
간단하게 설명하면 length만큼의 slice를 for문을 순회하면서 한번씩 해주는거예요. 이렇게 해서 나온 배열을 압축해주면 되는 것이죠. 참고로 위 코드대로 작동할 시에는 'aaabb' 라는 문자열이 있고 2개씩 slice를 해준다 하면 마지막 남는 b까지도 배열에 추가해주기 때문에 문제의 요구사항을 충족시킬 수 있답니다:)
이 부분의 코드를 작성하는 데에 시간이 조금 소요되었습니다. 위에서 만들어진 배열을 순회하면서 같은 배열이 나오게 되면 letter에 담고 count를 +1해주고, letter과 다른 문자가 나오게 되면 count를 1로 만들어 준 뒤에 result에 letter에 있던 문자와 count를 추가해주는 식으로 코드를 작성하였습니다. for문이 끝나고 나면 남아있는 letter과 count를 추가시켜주면 됩니다. 단, 반복되는 문자의 개수가 1인 경우에는 압축된 문자열에 표시하지 않기로 했기때문에 count가 1보다 큰 경우에만 추가시켜주면 돼요.
count=0 #반복되는 문자의 수를 체크할 변수
letter="" #반복되는 문자를 담을 변수
result="" #완성된 문자열을 담을 변수
for string in array:
if len(letter)==0:
letter=string
count+=1
elif len(letter)>0 and letter==string:
count+=1
elif len(letter)>0 and letter!=string:
if count>1:
result+=str(count)+letter
else:
result+=letter
letter=string
count=1
if count>1:
result+=str(count)+letter
else:
result+=letter
이렇게 해서 나온 result들의 길이를 구해서 최소값을 구해주면 됩니다!
그런데 이 경우 케이스 한개가 실패가 될거예요.
그래서 생각해보니 문자열의 길이가 1인 경우를 처리해주지 않았더군요. 그 부분까지 생각을해주어야 합니다.
if len(s)==1:
answer=1
최종 코드는 다음과 같습니다.
def solution(s):
answer = 0
resultArray=[]
if len(s)==1:
answer=1
for length in range(1,len(s)):
array=[s[i:i+length] for i in range(0, len(s), length)]
count=0 #반복되는 문자의 수를 체크할 변수
letter="" #반복되는 문자를 담을 변수
result="" #완성된 문자열을 담을 변수
for string in array:
if len(letter)==0:
letter=string
count+=1
elif len(letter)>0 and letter==string:
count+=1
elif len(letter)>0 and letter!=string:
if count>1:
result+=str(count)+letter
else:
result+=letter
letter=string
count=1
if count>1:
result+=str(count)+letter
else:
result+=letter
resultArray.append(len(result))
answer=min(resultArray)
return answer
카카오는 문자열 문제를 늘 하나씩 출제하는 것 같아요.
이 문제의 정답률은 25.8%였다고 공개되었는데요.
다른 문제들의 정답률이 10% 미만에서 소수점 %까지 있는 것으로 보아
기본적으로 문자열 처리 문제는 맞아야 합격할 수 있지 않을까 생각이 드네요.
간단하게 생각하고 접근하는 태도가 필요한 것 같습니다 :)
읽어주셔서 감사합니다.