https://programmers.co.kr/learn/courses/30/lessons/60059

 

코딩테스트 연습 - 자물쇠와 열쇠

[[0, 0, 0], [1, 0, 0], [0, 1, 1]] [[1, 1, 1], [1, 1, 0], [1, 0, 1]] true

programmers.co.kr

문제

문제 설명

고고학자인 튜브는 고대 유적지에서 보물과 유적이 가득할 것으로 추정되는 비밀의 문을 발견하였습니다. 그런데 문을 열려고 살펴보니 특이한 형태의 자물쇠로 잠겨 있었고 문 앞에는 특이한 형태의 열쇠와 함께 자물쇠를 푸는 방법에 대해 다음과 같이 설명해 주는 종이가 발견되었습니다.

잠겨있는 자물쇠는 격자 한 칸의 크기가 1 x 1 N x N 크기의 정사각 격자 형태이고 특이한 모양의 열쇠는 M x M 크기인 정사각 격자 형태로 되어 있습니다.

자물쇠에는 홈이 파여 있고 열쇠 또한 홈과 돌기 부분이 있습니다. 열쇠는 회전과 이동이 가능하며 열쇠의 돌기 부분을 자물쇠의 홈 부분에 딱 맞게 채우면 자물쇠가 열리게 되는 구조입니다. 자물쇠 영역을 벗어난 부분에 있는 열쇠의 홈과 돌기는 자물쇠를 여는 데 영향을 주지 않지만, 자물쇠 영역 내에서는 열쇠의 돌기 부분과 자물쇠의 홈 부분이 정확히 일치해야 하며 열쇠의 돌기와 자물쇠의 돌기가 만나서는 안됩니다. 또한 자물쇠의 모든 홈을 채워 비어있는 곳이 없어야 자물쇠를 열 수 있습니다.

열쇠를 나타내는 2차원 배열 key와 자물쇠를 나타내는 2차원 배열 lock이 매개변수로 주어질 때, 열쇠로 자물쇠를 열 수 있으면 true를, 열 수 없으면 false를 return 하도록 solution 함수를 완성해주세요.

제한사항

  • key는 M x M(3 ≤ M ≤ 20, M은 자연수)크기 2차원 배열입니다.
  • lock은 N x N(3 ≤ N ≤ 20, N은 자연수)크기 2차원 배열입니다.
  • M은 항상 N 이하입니다.
  • key와 lock의 원소는 0 또는 1로 이루어져 있습니다.
    • 0은 홈 부분, 1은 돌기 부분을 나타냅니다.

입출력 예

keylockresult

[[0, 0, 0], [1, 0, 0], [0, 1, 1]] [[1, 1, 1], [1, 1, 0], [1, 0, 1]] true

문제 해결을 위한 과정

이 문제의 경우 가장 중요한것은 자물쇠의 크기를 확장시켜야 한다는 것이었습니다. 그 이유는 문제에서 말해주듯이 자물쇠 영역을 벗어난 부분에 있는 열 쇠의 홈과 돌기는 자물쇠를 여는데 영향을 주지 않지만 이라는 문장 때문입니다. 이를 예시를 통해 알아봅시다. 그림은 다음과 같습니다. 파란색 사각형인 자물쇠를 벗어난 부분에 있는 빨간색 키의 부분은 영향을 지 않고 3 X 3 크기인 자물쇠가 딱 맞는 것을 확인할 수 있습니다. 이 문제에서는 이렇게 외부로 나가는 영역을 신경 쓰지 않고 자물쇠의 크기만 모두 차있으면 열림으로 처리힙니다. 

파란색 사각형 자물쇠 빨간색 사각형 열쇠

따라서 우리는 자물쇠의 크기를 확대시켜야 한다는것을 알 수 있습니다. 그렇다면 얼마나 키워야 하느냐가 가장 큰 주요한 문제가 될 것입니다. 문제의 조건을 보면 항상 자물쇠의 크기인 N이 열쇠의 크기인 M 이상임을 알 수 있기 때문에 열쇠의 크기는 신경을 쓰지 않고 자물쇠의 크기를 3배로 만들어주면 됩니다. 3배의 크기로 키우면 즉 3 x 3인 경우 9 x 9 크기로 만들어주게 되면 런타임 에러를 걱정하지 않고 키를 자물쇠의 좌상 단부터 우하단까지 모두 맞춰볼 수 있는 것입니다. 이제 문제를 부분으로 나누어 봅시다. 열쇠를 돌리는 부분, 자물쇠가 열리는지 확인하는 부분, 열쇠를 자물쇠에 맞춰보는 부분으로 3가지로 크게 나눌 수 있습니다. 이 부분에 대한 각각의 함수를 작성하면 쉽게 해결할 수 있었습니다. 이렇게 해결할 수 있는 이유는 이 문제가 M의 크기가 20, N의 크기가 20 이 각각의 최대 이므로 모든 경우를 계산하더라도 400회 이기 때문에 완전 탐색으로 해결할 수 있는 것입니다. 


문제 해결을 위한 팁

이차원 리스트를 복사 할 때 deepcopy를 이용하여야 합니다. deepcopy를 이용하기 위해서는 import copy를 넣어주어야 합니다.

소스코드 
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
29
30
31
32
33
34
35
36
37
38
39
import copy
def turn(key, m): # 이차원 리스트를 회전 시키는 함수
    turn_key = [[0]*for _ in range(m)]
    for i in range(m):
        for j in range(m):
            turn_key[i][j] = key[m-j-1][i]            
    return turn_key
 
def check(array): # 리스트를 조회하면서 1이 아닌 곳이 있으면 열린게 아니므로 바로 False
    num = len(array) // 3 
    for i in range(num, num *2):
        for j in range(num, num *2):
            if array[i][j] != 1:
                return False
    return True
 
def solution(key, lock): 
    n = len(lock)
    m = len(key)
    
    array = [[0]*(n*3for _ in range(n*3)]
    for i in range(n):
        for j in range(n):
            array[i+n][j+n] = lock[i][j]
    
    copy_key = copy.deepcopy(key)
    for i in range(4):
        copy_key = turn(copy_key, m)
        for i in range(n*2): # 3배를 확대하였기 때문에 첫번째 두번째 부분만 체크하면 됨
            for j in range(n*2):
                for a in range(m):
                    for b in range(m):
                        array[a+i][b+j] += copy_key[a][b]
                if check(array):
                    return True
                for a in range(m):
                    for b in range(m):
                        array[a+i][b+j] -= copy_key[a][b]
    return False
cs

 

'알고리즘 > 프로그래머스' 카테고리의 다른 글

블록 이동하기 (Python)  (0) 2020.12.07
괄호 변환 (Python)  (0) 2020.12.05
모의고사 (Level 1 Python)  (0) 2020.11.30
기둥과 보 설치 (Python)  (0) 2020.11.29
완주하지 못한 선수 (Level 1 Python)  (0) 2020.11.28

이 문제는 나동빈 님의 소스를 참고하여 해결하였습니다. 나동빈 님의 해당 문제 깃허브 링크 

https://github.com/ndb796/python-for-coding-test/blob/master/12/6.py

 

코딩테스트 연습 - 기둥과 보 설치

5 [[1,0,0,1],[1,1,1,1],[2,1,0,1],[2,2,1,1],[5,0,0,1],[5,1,0,1],[4,2,1,1],[3,2,1,1]] [[1,0,0],[1,1,1],[2,1,0],[2,2,1],[3,2,1],[4,2,1],[5,0,0],[5,1,0]] 5 [[0,0,0,1],[2,0,0,1],[4,0,0,1],[0,1,1,1],[1,1,1,1],[2,1,1,1],[3,1,1,1],[2,0,0,0],[1,1,1,0],[2,2,0,1]] [[

programmers.co.kr

https://programmers.co.kr/learn/courses/30/lessons/60061

문제 (그림자료는 위의 링크를 참조해주세요)

문제 설명

빙하가 깨지면서 스노우타운에 떠내려 온 죠르디는 인생 2막을 위해 주택 건축사업에 뛰어들기로 결심하였습니다. 죠르디는 기둥과 보를 이용하여 벽면 구조물을 자동으로 세우는 로봇을 개발할 계획인데, 그에 앞서 로봇의 동작을 시뮬레이션 할 수 있는 프로그램을 만들고 있습니다.
프로그램은 2차원 가상 벽면에 기둥과 보를 이용한 구조물을 설치할 수 있는데, 기둥과 보는 길이가 1인 선분으로 표현되며 다음과 같은 규칙을 가지고 있습니다.

  • 기둥은 바닥 위에 있거나 보의 한쪽 끝 부분 위에 있거나, 또는 다른 기둥 위에 있어야 합니다.
  • 보는 한쪽 끝 부분이 기둥 위에 있거나, 또는 양쪽 끝 부분이 다른 보와 동시에 연결되어 있어야 합니다.

단, 바닥은 벽면의 맨 아래 지면을 말합니다.

2차원 벽면은 n x n 크기 정사각 격자 형태이며, 각 격자는 1 x 1 크기입니다. 맨 처음 벽면은 비어있는 상태입니다. 기둥과 보는 격자선의 교차점에 걸치지 않고, 격자 칸의 각 변에 정확히 일치하도록 설치할 수 있습니다. 다음은 기둥과 보를 설치해 구조물을 만든 예시입니다.

예를 들어, 위 그림은 다음 순서에 따라 구조물을 만들었습니다.

  1. (1, 0)에서 위쪽으로 기둥을 하나 설치 후, (1, 1)에서 오른쪽으로 보를 하나 만듭니다.
  2. (2, 1)에서 위쪽으로 기둥을 하나 설치 후, (2, 2)에서 오른쪽으로 보를 하나 만듭니다.
  3. (5, 0)에서 위쪽으로 기둥을 하나 설치 후, (5, 1)에서 위쪽으로 기둥을 하나 더 설치합니다.
  4. (4, 2)에서 오른쪽으로 보를 설치 후, (3, 2)에서 오른쪽으로 보를 설치합니다.

만약 (4, 2)에서 오른쪽으로 보를 먼저 설치하지 않고, (3, 2)에서 오른쪽으로 보를 설치하려 한다면 2번 규칙에 맞지 않으므로 설치가 되지 않습니다. 기둥과 보를 삭제하는 기능도 있는데 기둥과 보를 삭제한 후에 남은 기둥과 보들 또한 위 규칙을 만족해야 합니다. 만약, 작업을 수행한 결과가 조건을 만족하지 않는다면 해당 작업은 무시됩니다.

벽면의 크기 n, 기둥과 보를 설치하거나 삭제하는 작업이 순서대로 담긴 2차원 배열 build_frame이 매개변수로 주어질 때, 모든 명령어를 수행한 후 구조물의 상태를 return 하도록 solution 함수를 완성해주세요.

제한사항

  • n은 5 이상 100 이하인 자연수입니다.
  • build_frame의 세로(행) 길이는 1 이상 1,000 이하입니다.
  • build_frame의 가로(열) 길이는 4입니다.
  • build_frame의 원소는 [x, y, a, b]형태입니다.
    • x, y는 기둥, 보를 설치 또는 삭제할 교차점의 좌표이며, [가로 좌표, 세로 좌표] 형태입니다.
    • a는 설치 또는 삭제할 구조물의 종류를 나타내며, 0은 기둥, 1은 보를 나타냅니다.
    • b는 구조물을 설치할 지, 혹은 삭제할 지를 나타내며 0은 삭제, 1은 설치를 나타냅니다.
    • 벽면을 벗어나게 기둥, 보를 설치하는 경우는 없습니다.
    • 바닥에 보를 설치 하는 경우는 없습니다.
  • 구조물은 교차점 좌표를 기준으로 보는 오른쪽, 기둥은 위쪽 방향으로 설치 또는 삭제합니다.
  • 구조물이 겹치도록 설치하는 경우와, 없는 구조물을 삭제하는 경우는 입력으로 주어지지 않습니다.
  • 최종 구조물의 상태는 아래 규칙에 맞춰 return 해주세요.
    • return 하는 배열은 가로(열) 길이가 3인 2차원 배열로, 각 구조물의 좌표를 담고 있어야 합니다.
    • return 하는 배열의 원소는 [x, y, a] 형식입니다.
    • x, y는 기둥, 보의 교차점 좌표이며, [가로 좌표, 세로 좌표] 형태입니다.
    • 기둥, 보는 교차점 좌표를 기준으로 오른쪽, 또는 위쪽 방향으로 설치되어 있음을 나타냅니다.
    • a는 구조물의 종류를 나타내며, 0은 기둥, 1은 보를 나타냅니다.
    • return 하는 배열은 x좌표 기준으로 오름차순 정렬하며, x좌표가 같을 경우 y좌표 기준으로 오름차순 정렬해주세요.
    • x, y좌표가 모두 같은 경우 기둥이 보보다 앞에 오면 됩니다.

입출력 예

nbuild_frameresult

5 [[1,0,0,1],[1,1,1,1],[2,1,0,1],[2,2,1,1],[5,0,0,1],[5,1,0,1],[4,2,1,1],[3,2,1,1]] [[1,0,0],[1,1,1],[2,1,0],[2,2,1],[3,2,1],[4,2,1],[5,0,0],[5,1,0]]
5 [[0,0,0,1],[2,0,0,1],[4,0,0,1],[0,1,1,1],[1,1,1,1],[2,1,1,1],[3,1,1,1],[2,0,0,0],[1,1,1,0],[2,2,0,1]] [[0,0,0],[0,1,1],[1,1,1],[2,1,1],[3,1,1],[4,0,0]]

입출력 예에 대한 설명

입출력 예 #1

문제의 예시와 같습니다.

입출력 예 #2

여덟 번째 작업을 수행 후 아래와 같은 구조물 만들어집니다.

아홉 번째 작업의 경우, (1, 1)에서 오른쪽에 있는 보를 삭제하면 (2, 1)에서 오른쪽에 있는 보는 조건을 만족하지 않으므로 무시됩니다.

열 번째 작업의 경우, (2, 2)에서 위쪽 방향으로 기둥을 세울 경우 조건을 만족하지 않으므로 무시됩니다.


문제 해결을 위한 과정

이 문제의 경우 build_frame의 길이 즉 명령의 횟수가 1000 이하이기 때문에 O(N^3)의 시간 복잡도를 가지는 방법으로도 해결할 수 있습니다. 먼저 x, y, a, b를 각 리스트에서 뽑아낸 후 b가 0 즉 삭제를 진행해야 한다면 일단 삭제를 진행한 후 보 혹은 기둥의 조건을 충족하는지 확인을 하면 됩니다. 만약 조건을 충족하지 못하면 삭제를 철회 즉 재설치를 하는 것입니다. 반대의 경우도 마찬가지입니다. 우선 설치를 진행한 후 보 혹은 기둥의 조건을 충족하는지 확인한 후 조건 충족 여부에 따라 재설정해주면 되는 것입니다. 


문제 해결을 위한 팁

사실 팁이라고 하기는 조금 거창하지만 예제의 result를 보면 오름차순으로 정렬이 되어있는 것을 확인 할 수 있습니다. 저 같은 경우는 문제에서 오름차순 조건이 없기에 정렬을 하지 않아서 틀렸습니다. 그 후 result를 보고 정렬이 되어있는것을 확인할 수 있었고 그 후 result를 정렬하여 정답을 도출했습니다.


소스코드

 

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
29
30
def check(ans):
    for i in range(len(ans)):
        x, y, a = ans[i]
        if a == 0# 기둥인 경우
            if y == 0 or [x-1, y, 1in ans or [x, y, 1in ans or [x, y-10in ans:
                continue
            else:
                return False
        if a == 1# 보 인경우
            if [x, y-10in ans or [x+1, y-10in ans or ([x-1, y, 1in ans and [x+1, y, 1in ans):
                continue
            else:
                return False
    return True
        
def solution(n, build_frame):
    ans = []
    for i in range(len(build_frame)):
        x, y, a, b = build_frame[i]
        if b == 0#삭제인 경우
            ans.remove([x, y, a])
            if check(ans) == False:
                ans.append([x, y, a])
        elif b == 1#설치인 경우
            ans.append([x, y, a]) 
            if check(ans) == False:
                ans.remove([x, y, a])
    return sorted(ans)
                   
 
cs
 

코딩테스트 연습 - 완주하지 못한 선수

수많은 마라톤 선수들이 마라톤에 참여하였습니다. 단 한 명의 선수를 제외하고는 모든 선수가 마라톤을 완주하였습니다. 마라톤에 참여한 선수들의 이름이 담긴 배열 participant와 완주한 선수

programmers.co.kr

https://programmers.co.kr/learn/courses/30/lessons/42576

문제

문제 설명

수많은 마라톤 선수들이 마라톤에 참여하였습니다. 단 한 명의 선수를 제외하고는 모든 선수가 마라톤을 완주하였습니다.

마라톤에 참여한 선수들의 이름이 담긴 배열 participant와 완주한 선수들의 이름이 담긴 배열 completion이 주어질 때, 완주하지 못한 선수의 이름을 return 하도록 solution 함수를 작성해주세요.

제한사항

  • 마라톤 경기에 참여한 선수의 수는 1명 이상 100,000명 이하입니다.
  • completion의 길이는 participant의 길이보다 1 작습니다.
  • 참가자의 이름은 1개 이상 20개 이하의 알파벳 소문자로 이루어져 있습니다.
  • 참가자 중에는 동명이인이 있을 수 있습니다.

입출력 예

participantcompletionreturn

[leo, kiki, eden] [eden, kiki] leo
[marina, josipa, nikola, vinko, filipa] [josipa, filipa, marina, nikola] vinko
[mislav, stanko, mislav, ana] [stanko, ana, mislav] mislav

입출력 예 설명

예제 #1
leo는 참여자 명단에는 있지만, 완주자 명단에는 없기 때문에 완주하지 못했습니다.

예제 #2
vinko는 참여자 명단에는 있지만, 완주자 명단에는 없기 때문에 완주하지 못했습니다.

예제 #3
mislav는 참여자 명단에는 두 명이 있지만, 완주자 명단에는 한 명밖에 없기 때문에 한 명은 완주하지 못했습니다.


문제 해결을 위한 과정

이 문제는 제가 생각하기엔 Level 1 문제 치고는 생각할것들이 많았던 문제였습니다. 리스트의 크기가 100,000이었기 때문에 일반적인 반복문으로 코드를 작성하면 시간 초과 판정을 받을 수 있기 때문입니다. 따라서 저의 경우는 두 리스트들을 set으로 집합의 형태로 바꾼후 차집합을 이용하는 방식으로 구하려 했습니다. 그러나 이 경우 예제 3번과 같은 방식에서 에러가 났습니다. 바로 동명이인이 있는 경우 집합을 사용하면 중복을 허용하지 않는 성질로 인해 동명이인이 합쳐지기 때문입니다. 따라서 Zip()을 이용하였습니다. zip()은 파이썬의 내장 함수로 동일한 개수의 자료형들이 있다면 그들을 묶어주는 역할을 합니다. 이렇게 작성을 하면 예제 #1의 경우 다음과 같이 묶입니다. (각 리스트들을 정렬을 해주어야 동일한 사람들끼리 묶이기 때문에 정렬은 필수 입니다.)

(eden, eden), (kiki, kiki)  - 그 뒤는 참가자 명단에는 leo가 있지만 완주자 명단에 없기 때문에 숫자가 맞지 않아서 묶이지 않음 이 경우 정렬된 참가자 명단의 가장 마지막 원소인 leo 출력을 하면 정답입니다.


소스코드

participant.sort();&nbsp;completion.sort()&nbsp;<span

1
2
3
4
5
6
def solution(participant, completion):
    participant.sort(); completion.sort() # 각각의 리스트 정렬
    for x, y in zip(participant, completion): # 각 리스트들의 원소를 zip으로 묶음
        if x != y: # 만약 x, y가 다르다면 x를 return
            return x
    return participant[-1# for문을 종료한 경우 참가자의 마지막을 return 
cs
 

코딩테스트 연습 - 두 개 뽑아서 더하기

정수 배열 numbers가 주어집니다. numbers에서 서로 다른 인덱스에 있는 두 개의 수를 뽑아 더해서 만들 수 있는 모든 수를 배열에 오름차순으로 담아 return 하도록 solution 함수를 완성해주세요. 제한

programmers.co.kr

https://programmers.co.kr/learn/courses/30/lessons/68644

문제

문제 설명

정수 배열 numbers가 주어집니다. numbers에서 서로 다른 인덱스에 있는 두 개의 수를 뽑아 더해서 만들 수 있는 모든 수를 배열에 오름차순으로 담아 return 하도록 solution 함수를 완성해주세요.


제한사항

  • numbers의 길이는 2 이상 100 이하입니다.
    • numbers의 모든 수는 0 이상 100 이하입니다.

입출력 예

numbersresult

[2,1,3,4,1] [2,3,4,5,6,7]
[5,0,2,7] [2,5,7,9,12]

입출력 예 설명

입출력 예 #1

  • 2 = 1 + 1입니다. (1이 numbers에 두 개 있습니다.)
  • 3 = 2 + 1 입니다.
  • 4 = 1 + 3입니다.
  • 5 = 1 + 4 = 2 + 3입니다.
  • 6 = 2 + 4입니다.
  • 7 = 3 + 4입니다.
  • 따라서 [2,3,4,5,6,7] 을 return 해야 합니다.

입출력 예 #2

  • 2 = 0 + 2입니다.
  • 5 = 5 + 0 입니다.
  • 7 = 0 + 7 = 5 + 2 입니다.
  • 9 = 2 + 7 입니다.
  • 12 = 5 + 7 입니다.
  • 따라서 [2,5,7,9,12] 를 return 해야 합니다.

문제 해결을 위한 과정

이 문제의 경우 Level 1 문제로 어렵지 않은 문제였습니다. 리스트로 주어진 숫자 중에서 2개의 숫자를 골라서 그 합을 출력을 하는 문제입니다. 입출력 예 #1을 보면 2, 1, 3, 1, 4인데 3 + 4 = 7, 4 + 3 = 7로 순서가 상관이 없으므로 순열이 아닌 조합의 방법을 사용해야 한다는 것을 알 수 있습니다. 따라서 해당 리스트에서 2개의 숫자를 골라서 그 합을 answer리스트에 넣어주면 됩니다.


문제 해결을 위한 팁

조합을 하여 계산을 하면 중복되는 것들이 문제가 됩니다. 입출력 예제 #2를 보면 5, 2를 뽑아서 5 + 2 = 7과 0 , 7을 뽑이서 합한 값인 0 + 7 = 7이 중복이 되는 것을 확인할 수 있습니다. 이 경우는 집합의 정리를 활용하면 쉽게 해결할 수 있습니다. 집합은 중복을 허용하지 않기 때문입니다. 따라서 set()을 이용하면 아주 손쉽게 중복을 제거할 수 있습니다. 그 후 출력방식에 맞게 다시 리스트로 변경을 해주면 됩니다.


소스코드

 

1
2
3
4
5
6
7
8
9
10
11
12
13
from itertools import combinations
 
def solution(numbers):
    answer = []
    for combination in combinations(numbers, 2): # numbers에서 2개의 숫자를 조합으로 뽑음
        # combination은 (2, 1) 같은 형태임
        x = int(combination[0]) 
        y = int(combination[1])
        answer.append(x+y) # 두 숫자의 합을 answer리스트에 넣어줌
    answer = set(answer) # 중복을 제거하기 위해 집합의 형태로 변환
    answer = list(answer) # 출력 조건을 맞추기 위해 리스트 형태로 변환
    answer.sort() # 오름차순으로 정렬
    return answer
cs
 

코딩테스트 연습 - 문자열 압축

데이터 처리 전문가가 되고 싶은 어피치는 문자열을 압축하는 방법에 대해 공부를 하고 있습니다. 최근에 대량의 데이터 처리를 위한 간단한 비손실 압축 방법에 대해 공부를 하고 있는데, 문자

programmers.co.kr

https://programmers.co.kr/learn/courses/30/lessons/60057

문제 설명

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

 

문제 해결을 위한 과정

이 문제의 경우 문제에서 주어진 조건은 아니지만 문제를 읽다 보면 유추할 수 있는 조건이 있습니다. 바로 문자열을 최대 몇 개의 단위로 잘라 압축할 수 있느냐입니다. 입출력 예 #1을 기준으로 설명을 드리면 aabbaccc의 길이 8을 2로 나는 몫 즉 최대 4개의 단위로 잘라 압축을 할 수 있다는 것입니다. 이유는 5개로 압축을 한다면 aabba / accc 즉 애초에 압축이 불가능하다는 것을 알 수 있습니다. 따라서 최대 단위는 입력받은 문자열 길이의 나누기 2 한 값입니다.

 

문제 해결을 위한 팁

계속해서 입출력 예 #1을 기준으로 설명을 드리겠습니다. 먼저 표는 다음과 같습니다.

단위  범위
1개 s[0:1] s[1:2] s[2:3] s[3:4] s[4:5] s[5:6] s[6:7]
2개 s[0:2] s[2:4] s[4:6] s[6:8]      
3개  s[0:3] s[3:6] s[6:8]        
4개 s[0:4] s[4:8]          

이 표를 기준으로 슬라이싱하는 인덱스들을 기준점으로 잡아서 소스코드를 작성하면 됩니다. 가장 먼저 단위는 1부터 4까지이기 때문에 이중 for문 중 바깥에 있는 for문은 for i in range(1, len(s)//2+1): 로 해주시면 됩니다.

다음 인덱스는 맨 처음 열을 제외하고 보면 슬라이싱의 시작 지점이 단위와 같은 것을 확인할 수 있습니다. ex) 단위 1개의 경우 첫 열을 제외하고 두 번째 열의 슬라이싱 시작 지점이 s[1:2]로 1 임, 단위 2개의 경우 첫 열을 제회하고 시작 지점이 s[2:4]로 2 임 다른 과정 역시 동일.

 

따라서 두 번째 for문은 for j in range(i, len(s)+1, i):로 작성하면 됩니다. for문에서 마지막 원소는 한 번에 i 만큼씩 뛰어넘겠다 라는 의미로 생각하면 됩니다. 이게 없는 경우 default로 1 만큼 뛰어넘는 일반적인 for문이 되는 것입니다.

 

마지막으로 두 번째 for문에서 왜 len(s)+1을 조건으로 하는지 궁금해하시는 분이 많을 텐데 이렇게 하면 마지막에 조회하는 s가 "" 즉 빈칸이 됩니다. 빈칸의 경우 무조건 이전 단계에서 슬라이싱으로 조회한 것과 다를 수밖에 없기 때문에 따로 마지막 원소에 대해 예외처리를 해줄 필요가 없다는 것이 장점입니다.

 

소스코드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
def solution(s):
    answer = len(s)
    for i in range(1len(s)//2+1):
        prev = s[0:0+i]
        count = 1
        ans = ""
        for j in range(i,len(s)+i,i): # 마지막 원소에 대해 따로 처리할 필요 없게 하기 위해 len(s)+1까지 
            now = s[j:j+i]
            if now == prev:
                count += 1
            else:
                if count != 1:
                    ans += (str(count)+prev)
                else:
                    ans += prev
                count = 1
            prev = now
        answer = min(answer, len(ans))
    return answer
cs

 

 

+ Recent posts