1. 문제
programmers.co.kr/learn/courses/30/lessons/17684
문제 설명
신입사원 어피치는 카카오톡으로 전송되는 메시지를 압축하여 전송 효율을 높이는 업무를 맡게 되었다. 메시지를 압축하더라도 전달되는 정보가 바뀌어서는 안 되므로, 압축 전의 정보를 완벽하게 복원 가능한 무손실 압축 알고리즘을 구현하기로 했다.
어피치는 여러 압축 알고리즘 중에서 성능이 좋고 구현이 간단한LZW(Lempel–Ziv–Welch) 압축을 구현하기로 했다. LZW 압축은 1983년 발표된 알고리즘으로, 이미지 파일 포맷인 GIF 등 다양한 응용에서 사용되었다.
LZW 압축은 다음 과정을 거친다.
- 길이가 1인 모든 단어를 포함하도록 사전을 초기화한다.
- 사전에서 현재 입력과 일치하는 가장 긴 문자열
w
를 찾는다. w
에 해당하는 사전의 색인 번호를 출력하고, 입력에서w
를 제거한다.- 입력에서 처리되지 않은 다음 글자가 남아있다면(
c
),w+c
에 해당하는 단어를 사전에 등록한다. - 단계 2로 돌아간다.
압축 알고리즘이 영문 대문자만 처리한다고 할 때, 사전은 다음과 같이 초기화된다. 사전의 색인 번호는 정수값으로 주어지며, 1부터 시작한다고 하자.
색인 번호 | 1 | 2 | 3 | ... | 24 | 25 | 26 |
단어 | A | B | C | ... | X | Y | Z |
예를 들어 입력으로KAKAO
가 들어온다고 하자.
- 현재 사전에는
KAKAO
의 첫 글자K
는 등록되어 있으나, 두 번째 글자까지인KA
는 없으므로, 첫 글자K
에 해당하는 색인 번호 11을 출력하고, 다음 글자인A
를 포함한KA
를 사전에 27 번째로 등록한다. - 두 번째 글자
A
는 사전에 있으나, 세 번째 글자까지인AK
는 사전에 없으므로,A
의 색인 번호 1을 출력하고,AK
를 사전에 28 번째로 등록한다. - 세 번째 글자에서 시작하는
KA
가 사전에 있으므로,KA
에 해당하는 색인 번호 27을 출력하고, 다음 글자O
를 포함한KAO
를 29 번째로 등록한다. - 마지막으로 처리되지 않은 글자
O
에 해당하는 색인 번호 15를 출력한다.
현재 입력(w) | 다음 글자(c) | 출력 | 사전 추가(w+c) |
K | A | 11 | 27: KA |
A | K | 1 | 28: AK |
KA | O | 27 | 29: KAO |
O | 15 |
이 과정을 거쳐 다섯 글자의 문장KAKAO
가 4개의 색인 번호 [11, 1, 27, 15]로 압축된다.
입력으로TOBEORNOTTOBEORTOBEORNOT
가 들어오면 다음과 같이 압축이 진행된다.
현재 입력(w) | 다음 글자(c) | 출력 | 사전 추가(w+c) |
T | O | 20 | 27: TO |
O | B | 15 | 28: OB |
B | E | 2 | 29: BE |
E | O | 5 | 30: EO |
O | R | 15 | 31: OR |
R | N | 18 | 32: RN |
N | O | 14 | 33: NO |
O | T | 15 | 34: OT |
T | T | 20 | 35: TT |
TO | B | 27 | 36: TOB |
BE | O | 29 | 37: BEO |
OR | T | 31 | 38: ORT |
TOB | E | 36 | 39: TOBE |
EO | R | 30 | 40: EOR |
RN | O | 32 | 41: RNO |
OT | 34 |
제한 사항
입력 형식
입력으로 영문 대문자로만 이뤄진 문자열msg
가 주어진다.msg
의 길이는 1 글자 이상, 1000 글자 이하이다.
출력 형식
주어진 문자열을 압축한 후의 사전 색인 번호를 배열로 출력하라.
입출력 예
msg | answer |
KAKAO | [11, 1, 27, 15] |
TOBEORNOTTOBEORTOBEORNOT | [20, 15, 2, 5, 15, 18, 14, 15, 20, 27, 29, 31, 36, 30, 32, 34] |
ABABABABABABABAB | [1, 2, 27, 29, 28, 31, 30] |
2. 어떻게 풀까?
Map
을 생성해서 A~Z까지index
1~26을 붙여 저장한다.- 문자열을
for
문으로 돌며Map
에 존재하는지 체크하고 있다면 문자열 길이를 늘이고, 없다면 이전 문자열에 해당하는index
값을 출력하고, 늘린 문자열은Map
에 추가한다.
3. 코드
테스트 코드
package programmers.level2.압축_20210111;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.*;
import org.junit.Test;
public class SolutionTest {
@Test
public void test1() {
assertThat(new Solution().solution("KAKAO"), is(new int[]{11, 1, 27, 15}));
}
@Test
public void test2() {
assertThat(new Solution().solution("TOBEORNOTTOBEORTOBEORNOT"),
is(new int[]{20, 15, 2, 5, 15, 18, 14, 15, 20, 27, 29, 31, 36, 30, 32, 34}));
}
@Test
public void test3() {
assertThat(new Solution().solution("ABABABABABABABAB"),
is(new int[]{1, 2, 27, 29, 28, 31, 30}));
}
}
실제 코드
package programmers.level2.압축_20210111;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class Solution {
public int[] solution(String msg) {
Map<String, Integer> dictionary = buildBasicDictionary();
List<Integer> indexList = new ArrayList<>();
int letterIndex = 27;
int startIndex = 0;
while (startIndex < msg.length()) {
int endIndex = getEndIndex(msg, dictionary, startIndex);
String target = msg.substring(startIndex, endIndex);
indexList.add(dictionary.get(target));
if (endIndex < msg.length()) {
dictionary.put(msg.substring(startIndex, endIndex + 1), letterIndex++);
}
startIndex = endIndex;
}
return indexList.stream().mapToInt(x -> x).toArray();
}
private int getEndIndex(String msg, Map<String, Integer> dictionary, int startIndex) {
int index;
for (index = startIndex + 1; index <= msg.length(); index++) {
if (!dictionary.containsKey(msg.substring(startIndex, index))) {
break;
}
}
return index - 1;
}
private Map<String, Integer> buildBasicDictionary() {
Map<String, Integer> map = new HashMap<>();
int index = 1;
for (char letter = 'A'; letter <= 'Z'; letter++, index++) {
map.put(letter + "", index);
}
return map;
}
}
buildBasicDictionary()
에서 A~Z까지Map
에 추가한다.while
문에서Map
에 존재하는 문자열의value
를List
에 저장하고, 존재하지 않는 문자열은Map
에 저장한다.getEndIndex()
에서endIndex
를 구한다.
4. 느낀 점
- 처음에
getEndIndex()
에서index
를 반환했다. 근데 그러면 입력 문자열의startIndex ~ endIndex - 1
까지의subString
은Map
에 존재하고,startIndex ~ endIndex
까지의subString
은 존재하지 않는다. 이름이 너무 애매하고 오히려 더 헷갈려서getEndIndex()
에서index - 1
을 반환하는것으로 해결했다. - 문제 자체는 카카오 문제치고는 어렵지 않은 편이다. level 2까지는 확실히 다 맞혀야 할듯
'Coding Test > Programmers' 카테고리의 다른 글
프로그래머스 - N으로 표현(02/02) (java) (0) | 2021.02.02 |
---|---|
프로그래머스 - 추석 트래픽(01/23) (java) (0) | 2021.01.23 |
프로그래머스 - 프렌즈 4블록 (12/22) (java) (1) | 2020.12.22 |
프로그래머스 - 뉴스 클러스터링 (12/10) (java) (0) | 2020.12.10 |
프로그래머스 - 키패드 누르기 (11/18) (java) (1) | 2020.11.18 |