Programmers Coding Test · Day 12 · Python
파이썬 슬라이싱 5문제 풀이
끝 인덱스를 포함하는 방법부터 누적 슬라이싱, 첫 위치와 마지막 위치 찾기까지 직접 작성한 코드의 수정 과정을 정리했다.
Day 12 문제들은 모두 배열을 자르는 문법을 사용하지만, 단순히 [시작:끝]만 외워서는 풀기 어려웠다. 끝 인덱스는 포함되지 않는다는 점, 현재 값과 반복 순서의 차이, 이전 작업 결과를 다음 작업에 반영해야 하는지를 함께 판단해야 했다.
리스트[시작:끝:간격] 형태로 사용한다. 닫힌 구간처럼 마지막 위치까지 포함해야 한다면 끝값에 1을 더하고, 연속 작업이라면 자른 결과를 원본 변수에 다시 저장해야 한다.
Day 12 문제와 공식 링크
| 순서 | 문제 | 핵심 개념 |
|---|---|---|
| 1 | 리스트 자르기 | [시작:끝:간격] |
| 2 | 첫 번째로 나오는 음수 | return, enumerate() |
| 3 | 배열 만들기 3 | 닫힌 구간, 리스트 연결 |
| 4 | 2의 영역 | 첫 위치와 마지막 위치 |
| 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을 먼저 적용했다.
n = 1처음부터 b번 인덱스까지이므로 num_list[:b + 1]과 같다.n = 2a번 인덱스부터 끝까지이므로 num_list[a:]를 사용한다.n = 3a부터 b까지 포함하므로 num_list[a:b + 1]이다.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
두 번째 시도와 너무 이른 반환
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
이 코드도 정답이다. 구간의 두 값을 a와 b로 바로 언패킹했고, 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
enumerate(range(...))의 값num에는 arr의 원소가 아니라 0, 1, 2 같은 인덱스 숫자가 들어간다. 배열 값을 함께 꺼내려면 enumerate(arr)를 사용해야 한다.arr[i:j]는 j번 인덱스를 포함하지 않는다. j까지 포함하려면 j + 1이 필요하다.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]
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
i는 query의 실제 값이다. 문제는 실제 값이 아니라 query에서 현재 처리 중인 인덱스가 짝수인지 확인해야 한다.arr.index(i)는 필요하지 않다query의 값은 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)의 차이를 구분한다.- 슬라이싱 결과는 이미 리스트이므로 불필요하게 대괄호로 다시 감싸지 않는다.
- 반복문의 변수에 실제 값이 들어오는지, 인덱스가 들어오는지 확인한다.
- 연속 작업은 직전 결과를 다음 작업에 다시 사용해야 하는지 확인한다.
같이 보면 좋은 개념
a, b, c = slicer처럼 정해진 구조의 값을 이름 있는 변수로 나누면 인덱스 접근을 줄일 수 있다.append()와 extend()값 하나를 추가할 때와 리스트의 여러 값을 이어 붙일 때의 차이를 함께 복습하면 좋다.이번 다섯 문제를 풀면서 슬라이싱 문법 자체보다 중요한 것은 범위와 상태를 정확히 읽는 일이라는 점을 확인했다. 시작점과 끝점을 어디까지 포함하는지, 현재 반복문의 변수가 값인지 인덱스인지, 잘라낸 결과가 다음 단계의 입력이 되는지를 먼저 적어 보면 복잡해 보이는 배열 문제도 훨씬 단순해진다.
'AI > Coding_Test' 카테고리의 다른 글
| Day 10) 프로그래머스 파이썬 문자열 5문제 풀이 (0) | 2026.07.17 |
|---|---|
| Day 10)프로그래머스 파이썬 풀이: 과일 장수, 소수 만들기, 오픈채팅방, 개미 군단, 컨트롤 제트 (0) | 2026.07.15 |
| Day 9) 프로그래머스 문자열 문제: 리스트 컴프리헨션, zip, 슬라이싱, 접미사 풀이 정리 (0) | 2026.07.14 |
| Day 8) 프로그래머스 조건문·문자열: 논리 연산, 주사위 게임 3, 문자열 만들기, 9로 나눈 나머지 (0) | 2026.07.13 |
| Day 7) 프로그래머스 반복문: 구간 쿼리, 배열 만들기, 콜라츠, 스택 풀이 정리 (0) | 2026.07.12 |