난이도 : 레벨 1
다트 점수 현황이 문자열로 주어질 때, 해당 문자열로부터 다트 점수를 계산하는 문자열 구현 문제이다.
내가 무식하게 푼 방법과 다른 사람의 코드를 보면서 배운 점을 적어보고자 한다.
평소에 구현을 무식하고 우직하게 하다보니 시간도 오래걸리고 실수도 정말 많이 해서 힘들었는데
이런 쉬운 깡구현 문제를 많이 풀면서 연습을 해야겠다는 생각이 들었다.
1. 다트는 3번 던진다.
2. 각 기회에서 점수는 0~10 사이의 점수가 주어진다.
3. 각 점수 이후에 해당 점수를 몇 제곱할지 S, D, T 가 주어진다.
4. 이후에 해당 점수와 기존 점수에 연산을 진행하는 옵션 *, #이 주어질 수도 있고 주어지지 않을 수도 있다.
def solution(dartResult):
point_list = []
is_last_digit = False
temp_point = 0
for c in dartResult:
if c.isdigit():
if not is_last_digit:
point_list.append(temp_point)
temp_point = 0
temp_point *= 10
temp_point += int(c)
is_last_digit = True
elif c == 'S':
temp_point **= 1
is_last_digit = False
elif c == 'D':
temp_point **= 2
is_last_digit = False
elif c == 'T':
temp_point **= 3
is_last_digit = False
elif c == '*':
point_list[-1] *= 2
temp_point *= 2
is_last_digit = False
elif c == '#':
temp_point *= -1
is_last_digit = False
point_list.append(temp_point)
answer = sum(point_list)
return answer
이게 내가 구현한 첫번째 깡구현 코드이다.
문제에서 하라고 한 걸 정직하게 다 하나하나 진행했다.
부끄럽지만 이 코드를 한번 뜯어보겠다ㅋㅋ
1. 다트는 3번 던진다
point_list = []
temp_point = 0
이 3번이라는 각각의 횟수의 점수를 저장하기위해 리스트를 이용했다.
리스트에 점수를 몰아서 저장해놓고 한번에 더해서 결과를 구할 생각이다.
그리고 계산 중인 각각의 점수를 임시로 저장할 변수를 하나 만들어주었다.
2. 각 기회에서 점수는 0~10 사이의 점수가 주어진다.
is_last_digit = False
for c in dartResult:
if c.isdigit():
if not is_last_digit:
point_list.append(temp_point)
temp_point = 0
temp_point *= 10
temp_point += int(c)
is_last_digit = True
만약 10점이 없었다면 그냥 isdigit 메서드로 숫자인지 확인해서 숫자면 바로 int 로 씌워서 넣었을 것이다.
그런데 10 이라는 두자리 수 하나를 처리하기 위해 나는 이렇게 힘들게 구현했다.
마지막으로 나온 문자가 숫자였는데, 이번에도 숫자가 나왔다면 이건 두자리수이니 두자리수로 계산하겠다는 의도로 이렇게 작성했다.
다른 사람들은 어떻게 했을까?
dartResult = dartResult.replace('10','k')
두자리수는 '10' 밖에 없으니까 이걸 그냥 단일 문자로 치환해버렸다ㅋㅋㅋ
..천잰데?
나는 ten 의 t로 치환해서 적용해봤다.
def solution(dartResult):
dartResult = dartResult.replace('10', 't')
point_list = []
temp_point = 0
for c in dartResult:
if c.isdigit():
point_list.append(temp_point)
temp_point = int(c)
elif c == 't':
point_list.append(temp_point)
temp_point = 10
elif c == 'S':
temp_point **= 1
elif c == 'D':
temp_point **= 2
elif c == 'T':
temp_point **= 3
elif c == '*':
point_list[-1] *= 2
temp_point *= 2
elif c == '#':
temp_point *= -1
point_list.append(temp_point)
answer = sum(point_list)
return answer
코드가 훨씬 간결해졌다.
is_last_digit = False
이 변수 하나가 사라지니까 디버깅도 쉽고 편해졌으며 가독성도 올라간다.
하지만 아직 더 고칠 점이 보인다.
3. 각 점수 이후에 해당 점수를 몇 제곱할지 S, D, T 가 주어진다.
나는 S, D, T 를 하나하나 분기해서 풀었지만, 다른 사람들은 S, D, T 를 이렇게 풀었다.
1. 리스트와 인덱스
sdt = ['S', 'D', 'T']
for j in point:
if j in sdt :
answer[i] = answer[i] ** (sdt.index(j)+1)
나보다 코드가 간결한건 좋은데, 내가 느끼기엔 좀 복잡하다.
리스트에 넣어두고 그 인덱스를 이용한다니..
그래도 이런 방법도 있다는 걸 배워갑니다ㅎㅎ
2. 딕셔너리
dart = {'S':1, 'D':2, 'T':3}
...
scores.append(int(dartResult[n:i])**dart[d])
난 이게 더 깔끔하다는 생각이 들어서 바로 적용해보았다.
def solution(dartResult):
dartResult = dartResult.replace('10', 't')
area_point = {'S': 1, 'D': 2, 'T': 3}
point_list = []
temp_point = 0
for c in dartResult:
if c.isdigit():
point_list.append(temp_point)
temp_point = int(c)
elif c == 't':
point_list.append(temp_point)
temp_point = 10
elif c in area_point:
temp_point **= area_point[c]
elif c == '*':
point_list[-1] *= 2
temp_point *= 2
elif c == '#':
temp_point *= -1
point_list.append(temp_point)
answer = sum(point_list)
return answer
분기문의 개수가 줄어들면서 훨씬 깔끔한 코드가 되었다.
4. 이후에 해당 점수와 기존 점수에 연산을 진행하는 옵션 *, #이 주어질 수도 있고 주어지지 않을 수도 있다.
'*' 처리에서 내가 작성한 코드는 '현재 점수와, 직전 점수에 대해 2를 곱한다' 라는 게 직관적이진 않다.
다른 사람들은 어떻게 했을까?
if d == "*":
scores[-2:] = [x*2 for x in scores[-2:]]
if d == "#":
scores[-1] = (-1)*scores[-1]
일단 점수를 저장해놓고 저장한 점수에 대해 연산을 진행했다.
elif j == '*':
answer[i] = answer[i] * 2
if i != 0 :
answer[i - 1] = answer[i - 1] * 2
elif j == '#':
answer[i] = answer[i] * (-1)
이 코드도 저장해놓고 했다.
나의 경우 점수를 저장하기 전에 이 연산을 진행했다.
직전 점수에 대해 2를 곱하는 건 리스트 가장 마지막 원소에 대해 연산을 진행했다.
내 방법은 실수하기 쉽고 복잡한 방법임이 분명하다.
그래서 나도 직관적으로 보고 이해할 수 있게 코드를 바꿔보았다.
나의 진짜_최종_코드
def solution(dartResult):
dartResult = dartResult.replace('10', 't')
area_point = {'S': 1, 'D': 2, 'T': 3}
point_list = []
temp_point = 0
for c in dartResult:
if c.isdigit():
temp_point = int(c)
elif c == 't':
temp_point = 10
elif c in area_point:
temp_point **= area_point[c]
point_list.append(temp_point)
elif c == '*':
point_list[-1] *= 2
if len(point_list) > 1:
point_list[-2] *= 2
elif c == '#':
point_list[-1] *= -1
answer = sum(point_list)
return answer
코드가 매우 직관적이다.
항상 점수는 area_point 와 같이 나오므로 이 계산이 끝나면 일단 점수 리스트에 점수를 넣고
그렇게 들어간 점수에 대해 '#' '*' 연산을 진행하도록 코드를 작성했다.
그랬더니 훨씬 더 직관적인 코드가 되었다.
마지막 - 여러가지 기교
어쨌거나 나는 제일 쉬운 분기문으로 풀었다.
다른 사람들이 푼 방법을 보니 여러가지 기교를 쓴 풀이도 보였다.
기교라고 표현하는게 맞나 모르겠지만 ㅋㅋ
1. 정규 표현식
bonus = {'S' : 1, 'D' : 2, 'T' : 3}
option = {'' : 1, '*' : 2, '#' : -1}
p = re.compile('(\d+)([SDT])([*#]?)')
dart = p.findall(dartResult)
for i in range(len(dart)):
if dart[i][2] == '*' and i > 0:
dart[i-1] *= 2
dart[i] = int(dart[i][0]) ** bonus[dart[i][1]] * option[dart[i][2]]
answer = sum(dart)
findall 을 하면 리스트를 만들어 주는구나.. 그렇구나..
2. enumerate
def solution(dartResult):
dart = {'S':1, 'D':2, 'T':3}
scores = []
n = 0
for i, d in enumerate(dartResult):
if d in dart:
scores.append(int(dartResult[n:i])**dart[d])
if d == "*":
scores[-2:] = [x*2 for x in scores[-2:]]
if d == "#":
scores[-1] = (-1)*scores[-1]
if not (d.isnumeric()):
n = i+1
return sum(scores)
오.. 인덱스와 같이 순회해서 SDT 를 기준으로 끊어서 점수 계산을 했다.
내가 맨 처음 작성한 문자열 파싱을 좀 더 고급지게 한 느낌이다.
레벨 1 문제인데도 배워갈 점들이 많다.
구현 연습을 할 땐 카카오 문자열 쌩구현 문제들을 풀면서 구현 연습을 하면 좋을 것 같다 ㅋㅋ
'알고리즘 (PS) > Programmers' 카테고리의 다른 글
[프로그래머스] 도넛과 막대 그래프 (2024 KAKAO WINTER INTERNSHIP) (0) | 2024.11.22 |
---|