Python 코딩테스트 학습 기록
이번 기록에서는 문제를 푸는 과정에서 자주 나오는 문자열 포함 검사, 반복문, 리스트 누적, 조건식, 2차원 배열 만들기를 정리했다. 정답 코드만 외우기보다 처음 작성한 코드가 왜 원하는 결과와 달라졌는지까지 함께 확인한다.
1. 커피 심부름
아메리카노와 anything은 4,500원, 카페라테가 포함된 메뉴는 5,000원이다. 처음에는 메뉴 이름을 각각 나누어 검사하는 방식으로 풀었다.
처음 풀이
def solution(order):
answer = 0
for menu in order:
if menu == "anything":
answer += 4500
elif "america" in menu:
answer += 4500
elif "cafelat" in menu:
answer += 5000
return answer
주어진 메뉴 이름에서는 동작하지만, 가격 규칙을 보면 기본 가격 4,500원에 라테일 때만 500원을 더하는 편이 더 간단하다. 또한 in은 문자열 안에 특정 문자열이 포함되어 있는지 검사한다.
기본 풀이
def solution(order):
answer = 0
for want in order:
if "latte" in want:
answer += 500
answer += 4500
return answer
"latte" in want가 참이면 라테이므로 500원을 추가한다. 그 뒤에는 모든 메뉴에 공통인 4,500원을 더한다.
짧은 풀이
def solution(order):
return sum(5000 if "latte" in menu else 4500 for menu in order)
A if 조건 else B는 조건이 참이면 A, 거짓이면 B를 선택하는 조건 표현식이다. 여기서는 각 메뉴의 가격을 만들고 sum()으로 합한다.
2. 그림 확대
그림을 k배 확대하려면 한 행의 각 문자를 가로로 k번 반복한 뒤, 완성된 행 자체를 세로로 k번 넣어야 한다.
처음 풀이와 문제점
def solution(picture, k):
answer = []
new_row = ""
for row in picture:
for pixel in row:
new_row += pixel * k
for _ in range(k):
answer.append(new_row)
return answer
new_row는 행마다 새로 만들어야 한다. 한 번 만든 행이 다음 행에도 이어지면 이전 행의 내용까지 누적된다. 또 return answer가 반복문 안에 있으면 첫 번째 행만 처리하고 함수가 끝난다.
수정한 기본 풀이
def solution(picture, k):
answer = []
for row in picture:
new_row = ""
for pixel in row:
new_row += pixel * k
for _ in range(k):
answer.append(new_row)
return answer
가로 확대가 끝난 new_row를 k번 추가하여 세로 확대를 처리한다. return은 모든 행이 끝난 뒤에 한 번만 실행해야 한다.
join()을 활용한 풀이
def solution(picture, k):
answer = []
for row in picture:
expanded = "".join(pixel * k for pixel in row)
for _ in range(k):
answer.append(expanded)
return answer
"".join(...)은 여러 문자열을 중간 구분자 없이 붙인다. 문자열을 순회하면 한 글자씩 나오므로 pixel에는 '.' 또는 'x'가 들어간다.
replace()를 활용한 풀이
def solution(picture, k):
answer = []
for i in range(len(picture)):
expanded = picture[i].replace(".", "." * k).replace("x", "x" * k)
answer += [expanded] * k
return answer
[expanded] * k는 같은 문자열을 k개 가진 리스트를 만든다. 문자열은 수정되지 않는 값이므로 이 문제에서는 안전하게 사용할 수 있다.
3. 조건에 맞게 수열 변환하기 3
k가 홀수이면 모든 원소에 k를 곱하고, 짝수이면 모든 원소에 k를 더한다.
기본 풀이
def solution(arr, k):
answer = []
for i in arr:
if k % 2 == 0:
answer.append(i + k)
else:
answer.append(i * k)
return answer
여기서 조건은 반복 중인 원소 i가 아니라 전체 기준값 k를 검사한다. k는 반복 전에 이미 정해져 있으므로, 반복문 밖에서 홀짝을 먼저 분기하는 방식도 가능하다.
리스트 컴프리헨션 풀이
def solution(arr, k):
return [i * k if k % 2 != 0 else i + k for i in arr]
이 문법은 [참일 때 값 if 조건 else 거짓일 때 값 for 원소 in 대상] 형태다. 모든 원소를 결과에 넣되 값만 바꾸므로 입력 배열과 결과 배열의 길이는 같다. 조건을 통과한 원소만 남기는 필터링 문법과는 다르다.
4. l로 만들기
문자열의 각 글자를 확인해 'l'보다 앞선 알파벳이면 'l'로 바꾸고, 그렇지 않으면 원래 글자를 유지하는 문제다.
리스트를 만들어 join() 하는 풀이
def solution(myString):
answer = [x if x > "l" else "l" for x in myString]
return "".join(answer)
문자열을 반복하면 x에는 한 글자씩 들어간다. 파이썬에서 같은 종류의 문자열은 알파벳 순서대로 비교할 수 있으므로 x > "l"은 x가 l보다 뒤에 있는지 확인한다.
짧은 풀이
def solution(myString):
return "".join(x if x > "l" else "l" for x in myString)
앞의 ""는 연결할 때 글자 사이에 아무것도 넣지 않겠다는 뜻이다. 예를 들어 ",".join(["a", "b", "c"])는 "a,b,c"가 되고, "".join(["a", "b", "c"])는 "abc"가 된다.
5. 특별한 이차원 배열 1
대각선에는 1, 나머지 위치에는 0이 들어가는 n × n 배열을 만드는 문제다.
처음 작성한 코드
def solution(n):
answer = [[]]
row = [0] * n
for i in range(n):
row[i] = 1
for j in range(n):
answer.append(row)
return row
이 코드에는 세 가지 점검할 부분이 있다. 첫째, 반환해야 하는 값은 한 행 row가 아니라 전체 배열 answer다. 둘째, 같은 row를 계속 추가하면 모든 행이 같은 리스트를 가리킨다. 셋째, 행 하나를 추가하는 데 안쪽 반복문이 필요하지 않아 행이 n × n개 추가된다.
수정 과정에서 남은 한 가지
def solution(n):
answer = [[]]
for i in range(n):
row = [0] * n
row[i] = 1
answer.append(row)
return answer
행을 반복마다 새로 만드는 부분은 맞다. 다만 answer = [[]]로 시작하면 빈 행 하나가 결과 맨 앞에 남는다. 빈 리스트에서 시작해야 한다.
기본 풀이
def solution(n):
answer = []
for i in range(n):
row = [0] * n
row[i] = 1
answer.append(row)
return answer
i번째 행에서는 i번째 칸만 1로 바꾼다. 매 반복마다 새 row를 만들기 때문에 각 행은 서로 독립적이다.
리스트 컴프리헨션 풀이
def solution(n):
return [[1 if i == j else 0 for j in range(n)] for i in range(n)]
바깥쪽 반복은 행 번호 i, 안쪽 반복은 열 번호 j를 뜻한다. 행 번호와 열 번호가 같은 위치만 대각선이므로 1을 넣는다.
마무리 체크
반복문으로 리스트나 문자열을 만들 때는 다음을 확인하면 실수를 줄일 수 있다.
1. 누적 변수는 자료형에 맞게 초기화했는가? 문자열은 "", 곱셈은 보통 1, 리스트는 []에서 시작한다.
2. return이 반복문 안에 들어가 조기에 종료되지 않는가?
3. 반복마다 새로 만들어야 하는 값이 이전 반복의 결과와 섞이지 않는가?
4. 조건이 반복 원소를 검사하는지, 전체 상태를 검사하는지 구분했는가?
5. 짧은 풀이를 쓴 뒤에도 직접 설명할 수 있는가?