본문 바로가기
AI/Coding_Test

Day 12) 프로그래머스 파이썬 슬라이싱 5문제 풀이

by 거대웅 TitanBear 2026. 7. 20.

Programmers Coding Test · Day 12 · Python

파이썬 슬라이싱 5문제 풀이

끝 인덱스를 포함하는 방법부터 누적 슬라이싱, 첫 위치와 마지막 위치 찾기까지 직접 작성한 코드의 수정 과정을 정리했다.

Day 12 문제들은 모두 배열을 자르는 문법을 사용하지만, 단순히 [시작:끝]만 외워서는 풀기 어려웠다. 끝 인덱스는 포함되지 않는다는 점, 현재 값과 반복 순서의 차이, 이전 작업 결과를 다음 작업에 반영해야 하는지를 함께 판단해야 했다.

오늘의 핵심 문법 슬라이싱은 리스트[시작:끝:간격] 형태로 사용한다. 닫힌 구간처럼 마지막 위치까지 포함해야 한다면 끝값에 1을 더하고, 연속 작업이라면 자른 결과를 원본 변수에 다시 저장해야 한다.

Day 12 문제와 공식 링크

프로그래머스 코딩 기초 트레이닝 Day 12
순서문제핵심 개념
1리스트 자르기[시작:끝:간격]
2첫 번째로 나오는 음수return, enumerate()
3배열 만들기 3닫힌 구간, 리스트 연결
42의 영역첫 위치와 마지막 위치
5배열 조각하기인덱스와 값, 누적 갱신

1. 리스트 자르기

slicer의 세 값을 a, b, c라고 할 때, n에 따라 시작점과 끝점, 간격을 다르게 적용하는 문제다.

내가 작성한 풀이

def solution(n, slicer, num_list):
    a, b, c = slicer
    b += 1

    if n == 1:
        return num_list[0:b]
    if n == 2:
        return num_list[a:]
    if n == 3:
        return num_list[a:b]
    if n == 4:
        return num_list[a:b:c]

이 풀이는 그대로 정답이다. a, b, c = slicer로 세 값을 한 번에 꺼냈고, 모든 경우에서 b번 인덱스까지 포함해야 하므로 공통으로 b += 1을 먼저 적용했다.

1
n = 1처음부터 b번 인덱스까지이므로 num_list[:b + 1]과 같다.
2
n = 2a번 인덱스부터 끝까지이므로 num_list[a:]를 사용한다.
3
n = 3a부터 b까지 포함하므로 num_list[a:b + 1]이다.
4
n = 4a부터 b까지 c 간격으로 선택하므로 num_list[a:b + 1:c]다.

공통 계산을 더 분명하게 쓴 풀이

def solution(n, slicer, num_list):
    a, b, c = slicer
    end = b + 1

    if n == 1:
        return num_list[:end]
    if n == 2:
        return num_list[a:]
    if n == 3:
        return num_list[a:end]
    return num_list[a:end:c]

b 자체를 바꾸는 대신 end라는 이름을 사용하면, 슬라이싱에서 제외되는 끝 위치를 저장했다는 의미가 더 분명하다. n은 1부터 4 중 하나이므로 앞의 세 조건이 아니면 마지막 경우는 자동으로 n = 4다.

2. 첫 번째로 나오는 음수

리스트를 앞에서부터 확인해 처음 만난 음수의 인덱스를 반환하고, 음수가 하나도 없다면 -1을 반환하는 문제다.

처음 작성한 코드

def solution(num_list):
    ans = 0

    for i in range(len(num_list)):
        if num_list[i] > 0:
            ans = -1
        elif num_list[i] < 0:
            ans = i

    return ans
문제점: 찾은 정답이 계속 덮어써진다 음수를 찾은 뒤에도 반복문이 끝까지 진행된다. 뒤에서 양수를 만나면 -1로 바뀌고, 다른 음수를 만나면 더 뒤의 인덱스로 바뀐다. 문제에서 필요한 값은 마지막 음수가 아니라 첫 번째 음수의 위치다. 모든 값이 0일 때 초기값 0이 그대로 반환되는 문제도 있다.

두 번째 시도와 너무 이른 반환

def solution(num_list):
    for i in range(len(num_list)):
        if num_list[i] > 0:
            return -1
        elif num_list[i] < 0:
            return i

음수를 발견했을 때 즉시 반환하는 방향은 맞다. 하지만 첫 번째 값이 양수라는 이유만으로 -1을 반환하면 뒤에 있는 음수를 확인하지 못한다. -1은 리스트 전체를 확인한 뒤에도 음수가 없을 때만 반환해야 한다.

수정한 기본 풀이

def solution(num_list):
    for i in range(len(num_list)):
        if num_list[i] < 0:
            return i

    return -1

더 읽기 쉬운 enumerate() 풀이

def solution(num_list):
    for i, num in enumerate(num_list):
        if num < 0:
            return i

    return -1

enumerate()는 인덱스와 값을 동시에 꺼낸다. num_list[i]로 다시 접근하지 않아도 되어 현재 값과 위치의 역할이 명확하다.

한 줄로 표현한 풀이

def solution(num_list):
    return next((i for i, num in enumerate(num_list) if num < 0), -1)

next()는 조건을 만족하는 첫 번째 인덱스를 반환한다. 해당 값이 없을 때 사용할 기본값으로 -1을 지정했다. 처음 배우는 단계에서는 반복문 풀이가 흐름을 확인하기 더 쉽다.

3. 배열 만들기 3

두 개의 닫힌 구간을 각각 잘라 앞뒤로 이어 붙이는 문제다. 닫힌 구간은 시작점과 끝점을 모두 포함한다.

내가 작성한 풀이

def solution(arr, intervals):
    answer = []

    for a, b in intervals:
        answer += arr[a:b + 1]

    return answer

이 코드도 정답이다. 구간의 두 값을 ab로 바로 언패킹했고, b번 인덱스까지 포함하기 위해 b + 1을 사용했다. 슬라이싱 결과는 리스트이므로 +=로 결과 리스트 뒤에 이어 붙일 수 있다.

리스트의 +는 원소별 덧셈이 아니다 기본 리스트에서 [1, 2, 3] + [4, 5, 6]의 결과는 [5, 7, 9]가 아니라 [1, 2, 3, 4, 5, 6]이다. 두 리스트를 순서대로 연결하기 때문에 이 문제에 사용할 수 있다.

구간이 두 개라는 조건을 그대로 사용한 풀이

def solution(arr, intervals):
    (s1, e1), (s2, e2) = intervals
    return arr[s1:e1 + 1] + arr[s2:e2 + 1]

문제에서 구간이 항상 두 개라고 보장하므로 이렇게 한 번에 언패킹할 수도 있다. 반면 처음 작성한 반복문 풀이는 구간 수가 늘어나도 그대로 사용할 수 있다는 장점이 있다.

리스트 컴프리헨션 풀이

def solution(arr, intervals):
    return [num for a, b in intervals for num in arr[a:b + 1]]

먼저 구간을 꺼내고, 해당 구간에서 숫자를 하나씩 꺼내 새 리스트에 담는다. 짧지만 반복 순서가 낯설 수 있어 이 문제에서는 원래 작성한 반복문 풀이도 충분히 좋다.

4. 2의 영역

배열에 있는 모든 2를 포함하는 가장 작은 연속 부분 배열을 반환한다. 결국 첫 번째 2의 위치부터 마지막 2의 위치까지 자르면 된다.

처음 생각한 풀이

def solution(arr):
    if arr.count(2) > 1:
        for i, num in enumerate(range(len(arr))):
            for j, num in enumerate(range(i + 1, len(arr))):
                if num == 2:
                    return arr[i:j]
    elif arr.count(2) == 1:
        for k, num in enumerate(range(len(arr))):
            if num == 2:
                return [arr[k]]
    else:
        return [-1]

    return answer
1
enumerate(range(...))의 값num에는 arr의 원소가 아니라 0, 1, 2 같은 인덱스 숫자가 들어간다. 배열 값을 함께 꺼내려면 enumerate(arr)를 사용해야 한다.
2
두 번째 2에서 멈추면 안 된다2가 세 개 이상이면 뒤의 2를 포함하지 못한다. 첫 번째 위치와 마지막 위치가 필요하다.
3
끝 인덱스가 빠진다arr[i:j]는 j번 인덱스를 포함하지 않는다. j까지 포함하려면 j + 1이 필요하다.
4
정의되지 않은 변수마지막의 return answer에서 answer를 만든 적이 없으므로 해당 경로에 도달하면 오류가 발생한다.

인덱스를 모으는 과정에서 생긴 실수

def solution(arr):
    num_list = []

    for i, num in enumerate(arr):
        if num == 2:
            num_list.append(i)

    if num_list == []:
        return [-1]
    elif len(num_list) == 1:
        return [arr[num_list[0]]
    elif len(num_list) == 2:
        return [arr[num_list[0]:num_list[1] + 1]]]
    else:
        return [arr[num_list[0]:num_list[-1]]]
괄호, 중첩 리스트, 끝점 오류 닫는 대괄호의 개수가 맞지 않아 문법 오류가 발생한다. 또한 arr[a:b]는 이미 리스트인데 다시 대괄호로 감싸면 [[...]] 형태의 중첩 리스트가 된다. 마지막 2까지 포함하려면 num_list[-1] + 1도 필요하다.

수정한 기본 풀이

def solution(arr):
    indices = []

    for i, num in enumerate(arr):
        if num == 2:
            indices.append(i)

    if not indices:
        return [-1]

    start = indices[0]
    end = indices[-1]
    return arr[start:end + 1]

2가 하나뿐이어도 indices[0]indices[-1]은 같은 위치다. 따라서 별도의 조건 없이 arr[start:start + 1]이 되어 [2]를 반환한다.

내가 완성한 짧은 풀이

def solution(arr):
    if 2 not in arr:
        return [-1]

    start = arr.index(2)
    end = len(arr) - arr[::-1].index(2)
    return arr[start:end]
왜 끝점에 따로 +1을 하지 않을까? 배열 길이를 L, 뒤집은 배열에서 마지막 2의 위치를 k라고 하면 원래 인덱스는 L - 1 - k다. 이 원소까지 포함하는 슬라이싱 끝점은 여기에 1을 더한 L - k다. 따라서 len(arr) - arr[::-1].index(2) 자체가 이미 끝 인덱스 다음 위치다.

마지막 원소가 2라면 뒤집은 배열에서 위치는 0이고 끝점은 len(arr)가 된다. 슬라이싱에서 arr[:len(arr)]는 마지막 인덱스 바로 다음까지 자르므로 마지막 원소도 정상적으로 포함한다.

5. 배열 조각하기

query를 순서대로 처리하면서, 짝수 번째 작업에서는 기준 위치 뒤를 버리고 홀수 번째 작업에서는 기준 위치 앞을 버린다. 매번 남은 배열을 다시 다음 작업의 대상으로 사용한다.

처음 작성한 코드

def solution(arr, query):
    answer = []

    for i in query:
        n = arr.index(i)

        if i % 2 == 0:
            answer = arr[:n + 1]
        else:
            answer = arr[n:]

    return answer
1
짝수·홀수 기준이 다르다i는 query의 실제 값이다. 문제는 실제 값이 아니라 query에서 현재 처리 중인 인덱스가 짝수인지 확인해야 한다.
2
arr.index(i)는 필요하지 않다query의 값은 arr에서 찾을 값이 아니라 배열을 자를 위치 자체다.
3
자른 결과를 누적해야 한다다음 작업은 원본 arr가 아니라 직전 작업에서 남은 배열을 대상으로 한다. 결과를 arr에 다시 저장해야 한다.

두 가지를 수정한 중간 풀이

def solution(arr, query):
    for value in query:
        if value % 2 == 0:
            arr = arr[:value + 1]
        else:
            arr = arr[value:]

    return arr

arr.index()를 제거하고 결과를 arr에 다시 저장한 점은 맞다. 하지만 여전히 query의 값이 짝수인지 검사하고 있다. 예제 [4, 1, 2]에서는 값의 홀짝과 인덱스의 홀짝이 우연히 같아 보이지만 다른 입력에서는 틀린다.

최종 수정 풀이

def solution(arr, query):
    for index, value in enumerate(query):
        if index % 2 == 0:
            arr = arr[:value + 1]
        else:
            arr = arr[value:]

    return arr

index는 현재 작업 순서의 홀짝을 판단하고, value는 실제 슬라이싱 위치로 사용한다. 짝수 번째 작업에서는 기준 위치를 남겨야 하므로 value + 1까지 자르고, 홀수 번째 작업에서도 기준 위치는 남겨야 하므로 arr[value:]를 사용한다.

공식 예제 흐름 [0, 1, 2, 3, 4, 5]에 query [4, 1, 2]를 적용하면 [0, 1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3] 순서로 배열이 갱신된다.

공식 예제로 검산하기

대표 입력과 결과
문제대표 입력결과
리스트 자르기n=4, slicer=[1, 5, 2][2, 4, 6]
첫 번째 음수[12, 4, 15, 46, 38, -2, 15]5
배열 만들기 3[1,2,3,4,5], [[1,3],[0,4]][2,3,4,1,2,3,4,5]
2의 영역[1,2,1,4,5,2,9][2,1,4,5,2]
배열 조각하기[0,1,2,3,4,5], [4,1,2][1,2,3]

실수 방지 체크리스트

  • 슬라이싱의 끝 인덱스는 결과에 포함되지 않는다.
  • 닫힌 구간의 끝값까지 필요하면 보통 end + 1을 사용한다.
  • 첫 번째 값을 찾는 문제에서는 발견한 순간 반환할 수 있는지 확인한다.
  • 실패 결과는 반복문 안이 아니라 전체 탐색이 끝난 뒤 반환해야 하는 경우가 많다.
  • enumerate(range(...))enumerate(arr)의 차이를 구분한다.
  • 슬라이싱 결과는 이미 리스트이므로 불필요하게 대괄호로 다시 감싸지 않는다.
  • 반복문의 변수에 실제 값이 들어오는지, 인덱스가 들어오는지 확인한다.
  • 연속 작업은 직전 결과를 다음 작업에 다시 사용해야 하는지 확인한다.

같이 보면 좋은 개념

1
언패킹a, b, c = slicer처럼 정해진 구조의 값을 이름 있는 변수로 나누면 인덱스 접근을 줄일 수 있다.
2
append()extend()값 하나를 추가할 때와 리스트의 여러 값을 이어 붙일 때의 차이를 함께 복습하면 좋다.
3
Early Return정답을 확정할 수 있는 순간과 아직 탐색을 계속해야 하는 순간을 구분하는 연습이 필요하다.
마무리

이번 다섯 문제를 풀면서 슬라이싱 문법 자체보다 중요한 것은 범위와 상태를 정확히 읽는 일이라는 점을 확인했다. 시작점과 끝점을 어디까지 포함하는지, 현재 반복문의 변수가 값인지 인덱스인지, 잘라낸 결과가 다음 단계의 입력이 되는지를 먼저 적어 보면 복잡해 보이는 배열 문제도 훨씬 단순해진다.