안녕하세요.
이번에는 다트 게임이라고 하는 문제를 풀어보려고 합니다.
저는 문제에 나온 그대로 구현을 해봤는데, 다른 답안을 보니 정규표현식을 이용해서 푼 답안이 있어 이를 같이 소개해드리고자 합니다.
정규식은 다양한 문자열 관련 문제에 활용될 수 있는 만큼, 이번 기회에 잘 알아두면 도움이 많이 될 것 같습니다.
https://school.programmers.co.kr/learn/courses/30/lessons/17682
제가 푼 풀이:
def solution(dartResult):
answer = 0
num = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
s_ = [] # score 저장
b_ = [] # 보너스 저장
o_ = [] # 옵션 저장
# 문자열이 존재하는 경우 while문
while dartResult:
# 숫자 관련
if len(dartResult) >= 2 and str(dartResult[:2]) == '10':
s_.append(10); dartResult = dartResult[2:] # 첫 번째 숫자 10으로 빼고 문자열 밀기
elif int(dartResult[0]) in num:
s_.append(int(dartResult[0])); dartResult = dartResult[1:]
# 보너스 관련
if str(dartResult[0]) == 'S':
b_.append('S')
elif str(dartResult[0]) == 'D':
b_.append('D')
elif str(dartResult[0]) == 'T':
b_.append('T')
dartResult = dartResult[1:] # 얘는 공통이라
# 옵션 관련
if len(dartResult) >= 1 and str(dartResult[0]) == '*':
o_.append('*')
dartResult = dartResult[1:]
elif len(dartResult) >= 1 and str(dartResult[0]) == '#':
o_.append('#')
dartResult = dartResult[1:]
else:
o_.append(" ")
sol_ = []
for i in range(3):
# 제곱 작업 우선 진행
if b_[i] == 'S':
sol_.append(s_[i] ** 1)
elif b_[i] == 'D':
sol_.append(s_[i] ** 2)
elif b_[i] == 'T':
sol_.append(s_[i] ** 3)
for i in range(3):
# 아무것도 없는 경우, 아무 작업 안함
if o_[i] == " ":
continue
# 첫 번째 나오고 스타상인 경우
if o_[i] == '*' and i == 0:
sol_[i] = sol_[i] * 2
elif o_[i] == '*' and i != 0:
sol_[i-1] = sol_[i-1] * 2 # 전에 얻은 점수 2배
sol_[i] = sol_[i] * 2 # 해당 점수 2배
# 아차상
if o_[i] == '#':
sol_[i] = sol_[i] * -1
return sum(sol_)
딱 봐도 문제 풀이 자체가 매우 긴 것을 확인하실 수 있습니다.
정직하게 문제에 나온 그대로를 코드로 구현한 것이라고 볼 수 있는데요.
score와 보너스, 옵션을 저장할 list를 별도로 지정하고 문자열에 대해서 while문을 돌면서 이 문자열을 score와 보너스, 옵션으로 분해하는 작업을 우선적으로 진행합니다.
먼저 숫자 관련 부분을 보시면,
while dartResult:
# 숫자 관련
if len(dartResult) >= 2 and str(dartResult[:2]) == '10':
s_.append(10); dartResult = dartResult[2:] # 첫 번째 숫자 10으로 빼고 문자열 밀기
elif int(dartResult[0]) in num:
s_.append(int(dartResult[0])); dartResult = dartResult[1:]
문자열의 길이가 2 이상이고(해당 조건 없으면 dartResult [:2]에서 index 에러가 발생합니다), 앞에 두 글자가 10인 경우는 score 부분을 10으로 append 하고, 문자열을 밀어줍니다.
10이 아닌 0부터 9인 경우는 in num으로 조건을 걸어서 숫자를 빼는 식으로 진행했습니다.
# 보너스 관련
if str(dartResult[0]) == 'S':
b_.append('S')
elif str(dartResult[0]) == 'D':
b_.append('D')
elif str(dartResult[0]) == 'T':
b_.append('T')
dartResult = dartResult[1:] # 얘는 공통이라
보너스 관련해서는 그다음 문자열이 S D T 중 하나이면 이를 보너스 관련 list에 추가해 주고, 문자열을 하나 밀었습니다.
S D T 중 뭐든 간에 1칸을 밀어주니 if 문에 넣지 않고 if문 바깥쪽에 미는 코드를 위치했습니다.
# 옵션 관련
if len(dartResult) >= 1 and str(dartResult[0]) == '*':
o_.append('*')
dartResult = dartResult[1:]
elif len(dartResult) >= 1 and str(dartResult[0]) == '#':
o_.append('#')
dartResult = dartResult[1:]
else:
o_.append(" ")
옵션 관련해서는 * 이거나 #인 경우 이를 옵션 관련 list에 추가하도록 했습니다.
len을 점검하는 코드가 있는 이유는, 옵션은 선택사항이라 옵션 자체가 아예 없는 경우가 있습니다. 이럴 때는 dartResult에 문자열 자체가 안 남아버려서 dartResult [0]로 접근이 안되고 index 에러가 발생합니다. 따라서 이를 방지하고자 문자열이 1개 이상 남은 경우에만 뽑아내도록 했습니다.
sol_ = []
for i in range(3):
# 제곱 작업 우선 진행
if b_[i] == 'S':
sol_.append(s_[i] ** 1)
elif b_[i] == 'D':
sol_.append(s_[i] ** 2)
elif b_[i] == 'T':
sol_.append(s_[i] ** 3)
그다음 부분은 먼저 제곱 작업을 우선적으로 진행해서, 값들을 sol_에 추가했습니다.
for i in range(3):
# 아무것도 없는 경우, 아무 작업 안함
if o_[i] == " ":
continue
# 첫 번째 나오고 스타상인 경우
if o_[i] == '*' and i == 0:
sol_[i] = sol_[i] * 2
elif o_[i] == '*' and i != 0:
sol_[i-1] = sol_[i-1] * 2 # 전에 얻은 점수 2배
sol_[i] = sol_[i] * 2 # 해당 점수 2배
# 아차상
if o_[i] == '#':
sol_[i] = sol_[i] * -1
return sum(sol_)
옵션에 별도로 없는 경우는 아무 작업 안 하고 continue로 넘어가도록 하였고,
*이 첫 번째 나오는 경우는 자기 자신의 값만 2배, *이 첫 번째가 아닌 곳에 나오는 경우는 전에 얻은 점수와 지금 점수를 모두 2배 하도록 했습니다.
그리고 #이 나오는 경우는 -1을 곱해주도록 했고, 마지막에는 sum 해서 결과가 나오도록 했습니다.
어떻게 보면 문제에 나온 내용을 말 그대로 충실히 진행해서 나온 코드라고 보시면 될 것 같습니다.
하지만 코드 수가 길고, 효율적이지는 못해서 더 효율적인 코드가 필요합니다.
개선된 코드:
import re
def solution(dartResult):
bonus = {'S' : 1, 'D' : 2, 'T' : 3}
option = {'' : 1, '*' : 2, '#' : -1}
p = re.compile('([0-9]+)([SDT])([*#]?)')
dart = p.findall(dartResult)
for i in range(len(dart)):
if dart[i][2] == '*' and i > 0: # i가 0보다 클때는 전에꺼 2배 해줌
dart[i-1] *= 2
dart[i] = int(dart[i][0]) ** bonus[dart[i][1]] * option[dart[i][2]] # 보너스로 제곱해주고 option에 맞게 곱해줌
answer = sum(dart)
return answer
개선된 코드는 정규표현식을 활용한 코드입니다.
이는 p = re.compile() 코드를 보시면 알 수 있습니다.
정규표현식을 compile하는 re.compile은 ( )를 통해 구분이 되는데요. 즉, 해당 코드에서는 크게 ([0-9]+) 부분과 ([SDT]) 부분, ([*#]?) 부분으로 총 3 부분으로 나눠집니다.
[0-9]는 0부터 9까지 숫자 중에 일치되는 경우를 찾는 정규표현식입니다. 여기서 +는 1개 이상을 뜻하므로, ([0-9]+)는 숫자이면서 1개 이상인 경우를 찾게 됩니다.
+가 들어가서 0~9인 숫자 뿐만 아니라 10인 경우도 찾을 수 있게 됩니다.
다음으로 ([SDT])는 S D T 중에 일치되는 경우를 찾게 되는 정규표현식입니다.
마지막으로 ([*#]?)는 두 번째 정규표현식 파트와 비슷하니 대충 유추하실 수 있겠지만, *이나 #인 경우를 찾습니다.
단, ? 는 나오지 않을 수도 있고, 1번 나올 수도 있습니다.
문제에서 옵션의 경우 *이거나 #이고, 혹은 아예 없을 수 있다고 했기 때문에, 이를 구현하기 위해 ?를 사용했다고 볼 수 있습니다.
1개 이상일 때는 +를, 0개 혹은 1개 일 때는 ?를 사용한다고 기억하면 좋겠습니다.
그러고 나서 p.findall()를 활용해 dartResult에서 compile 한 정규표현식 규칙을 만족하는 경우를 모두 찾은 것을 dart에 저장합니다.
dart를 print 해보면 다음과 같이 나오는 것을 확인할 수 있습니다.
보시면 전체적으로는 list이고, 안에는 3개의 element로 이루어진 튜플인 것을 알 수 있습니다.
이에 대해서 for문을 수행하게 됩니다.
다음으로 if dart[i][2] == '*' and i > 0: 부분인데요. 이는 i가 0보다 클 때는 *이 적용되었을 때 이전 score에도 2배를 해줘야 하기 때문에 dart[i-1] *= 2를 해준다고 보시면 됩니다.
그리고 dart[i] = int(dart[i][0]) ** bonus[dart[i][1]] * option[dart[i][2]]로 구현을 해서 최종 마지막 점수를 내는 것을 확인할 수 있습니다.
score 값에 bonus에 해당하는 dictionary에서 찾아 이를 제곱으로 곱해주고, 마지막으로 option에 해당하는 *이나 #인 경우는 2배 혹은 -1배를 적용해 주는 코드입니다.
마지막으로는 sum을 해서 결과를 내주면 됩니다.
확실히 정규표현식을 활용하면 조금 더 손쉽게 score와 보너스, 옵션을 찾을 수 있었고, 이를 dictionary를 활용해서 간편하게 최종 점수를 계산할 수 있었습니다.
이번 문제는 여기까지 하도록 하겠습니다.
'코딩테스트 준비 > 프로그래머스' 카테고리의 다른 글
프로그래머스 여섯 번째 문제: 기사단원의 무기(Level 1) (1) | 2023.04.23 |
---|---|
프로그래머스 다섯 번째 문제: 덧칠하기(Level 1) (0) | 2023.04.23 |
프로그래머스 세 번째 문제: 실패율(Level 1) (2) | 2023.03.12 |
프로그래머스 두 번째 문제: K번째수(Level 1) (0) | 2023.03.01 |
프로그래머스 첫 번째 문제: 문자열 내 마음대로 정렬하기(Level 1) (0) | 2023.02.26 |