본문 바로가기
Problem Solving/프로그래머스

Lv1. 2018 KAKAO BLIND RECRUITMENT[1차] 다트 게임 [X]

by 파피 2020. 11. 6.

문제 출처 : programmers.co.kr/learn/courses/30/lessons/17682

 

코딩테스트 연습 - [1차] 다트 게임

 

programmers.co.kr

1. 문제 이해

다트 게임은 총 3번의 기회로 구성

각 기회마다 얻을 수 있는 점수는 0점에서 10점

점수와 함께 싱글, 더블, 트리플 영역이 존재하고

각 영역 당첨 시 점수에서 1제곱, 2제곱, 3제곱 (점수^1 , 점수^2 , 점수^3 )으로 계산됨

스타상(*) 당첨 시 해당 점수와 바로 전에 얻은 점수를 각 2배 (맨 처음에 나올 경우 첫번째 스타상의 점수만 2배)

아차상(#) 당첨 시 해당 점수는 마이너스

스타상의 효과는 다른 스타상의 효과와 중첩 가능 -> 중첩된 스타상 점수 4배

스타상과 아차상의 효과 중첩 가능 -> 중첩된 아차상의 점수는 -2배

 

싱글, 더블, 트리플은 점수마다 하나씩 존재

스타상, 아차상은 점수마다 둘 중 하나만 존재할 수 있으며, 존재하지 않을 수도 있음

 

점수|보너스|옵션으로 이루어진 문자열 3세트가 입력될 시

총 점수를 반환

 

점수는 0에서 10사이

보너스는 S,D,T중 하나

옵션은 * 또는 # 또는 없음

 

2. 풀이 계획

뒤에서부터 풀어야할 것 같긴한데 잘 모르겠다.

 

3. 구현

4. 검토 및 개선

def solution(dartResult):
    bonus = {'S' : 1, 'D' : 2, 'T' : 3}
    option = {'*' : 2, '#' : -1}
    
    # 각 계산값이 변할 수 있기 때문에 세 분기의 합을 따로 구하기
    a = [0, 0, 0] # 맨 처음 배열 초기화
    flag = -1 # 배열의 인덱스
    
    for idx, dart in enumerate(dartResult) :
        if dart.isdigit() : # isdigit() : 문자열이 숫자인지 판단
            flag += 1
            if dart == '0' :
                continue
            elif dartResult[idx + 1].isdigit() : # 점수 10일때 처리
                a[flag] = int(dart) * 10
                flag -= 1 # 다음 for문 돌 때도 숫자가 있으므로 flag 증가시켜주기때문에 미리 감소시켜줌
            else :
                a[flag] = int(dart)
                
        elif dart in 'SDT' :
            a[flag] **= bonus[dart]
        
        else :
            if dart == '*' and flag > 0:
                a[flag-1] *= 2
            
            a[flag] *= option[dart]
    
    return sum(a)

출처 : ychae-leah.tistory.com/196?category=332760

 

 

c.f. 숫자가 10일때를 처리하기위해 다음과같은 방식을 사용할 수도 있다.

def solution(dartResult):
    dartResult = dartResult.replace('10', 'k') # 주의 : replace는 새로운 배열을 반환하므로 저장해줘야한다.
    dart = ['10' if i == 'k' else i for i in dartResult]
    bonus = {'S' : 1, 'D' : 2, 'T' : 3}
    option = {'*' : 2, '#' : -1}
    
    i = -1
    answer = [0, 0, 0]
    for j in dart :
        if j.isdigit() :
            i += 1
            answer[i] = int(j)
        elif j in ('S', 'D', 'T') :
            answer[i] **= bonus[j]
        elif j in ('*', '#') :
            if i > 0 and j == '*':
                answer[i-1] *= 2
            answer[i] *= option[j]
    return sum(answer)

 

## 정규표현식 이용한 풀이 ##

# 틀린코드 (주의사항)
# 튜플을 변경하고 마지막에 계산하려고했음

import re

def solution(dartResult):
    bonus = {'S' : 1, 'D' : 2, 'T' : 3}
    option = {'*' : 2, '#' : -1, '' : 1}
    p = re.compile('(\d+)([SDT])([*#]?)')
    dart = p.findall(dartResult)
    
    print(dart)
    
    for i in range(len(dart)) :
        if i > 0 and dart[i][2] == '*' :
            dart[i-1][0] = str(int(dart[i-1][0]) * 2) # 튜플은 변경 못함
    
    answer = 0
    for d in dart :
        answer += int(d[0]) ** bonus[d[1]] * bonus[d[2]]
        
    return answer
# 정규표현식 이용한 풀이

import re

def solution(dartResult):
    bonus = {'S' : 1, 'D' : 2, 'T' : 3}
    option = {'*' : 2, '#' : -1, '' : 1}
    p = re.compile('(\d+)([SDT])([*#]?)')
    # 주의: \d는 [0-9]와 동일한 표현식이므로 [] 써줄필요 없음
    # 주의: ()안넣어주면 튜플로 반환 안해줌
    dart = p.findall(dartResult)
    
    for i in range(len(dart)) :
        if i > 0 and dart[i][2] == '*' :
            dart[i-1] *= 2 # 이해되지 않았던 부분
        
        dart[i] = int(dart[i][0]) ** bonus[dart[i][1]] * option[dart[i][2]]
        
    return sum(dart)

위의 풀이에서 dart[i-1][2]가 이해되지않았다.

dart는 분명히 [('1', 'S', ''), ('2', 'D', '*'), ('3', 'T', '')] 이런 튜플의 배열일텐데

어떻게 곱하기 연산을 해서 int값이 나올 수 있나했더니

그 다음 줄에 i번째 튜플을 계산한뒤 아예 값으로 치환하기 때문이었다 ...!!!!!!

 

+) 정규 표현식

정규 표현식(Regular Expressions)은 복잡한 문자열을 처리할 때 사용하는 기법으로,

파이썬만의 고유 문법이 아니라 문자열을 처리하는 모든 곳에서 사용한다. (모든 언어 공통)

※ 정규 표현식은 줄여서 간단히 "정규식"이라고도 말한다.

정규 표현식이 필요한 이유

주민등록번호를 포함하고 있는 텍스트에 포함된 모든 주민등록번호의 뒷자리를 * 문자로 변경하는 코드

# 여러줄로 된 문자열 사용 ('''문자열''' 또는 """문자열""")
data = """
park 800905-1049118
kim  700905-1059119
"""

result = []
for line in data.split("\n"):
    word_result = []
    for word in line.split(" "): # 전체 텍스트를 공백 문자로 나눈다.
        if len(word) == 14 and word[:6].isdigit() and word[7:].isdigit(): # 나뉜 단어가 주민등록번호 형식이면
            word = word[:6] + "-" + "*******" # 주민등록번호 뒷자리를 *로 변환한다.
        word_result.append(word)
    result.append(" ".join(word_result)) # 나뉜 단어를 다시 조립한다.
print("\n".join(result))

# 결과값:
# park 800905-*******
# kim  700905-*******

 

반면에 정규식을 사용하면 다음처럼 훨씬 간편하고 직관적인 코드를 작성할 수 있다.

만약 찾으려는 문자열 또는 바꾸어야 할 문자열의 규칙이 매우 복잡하다면 정규식의 효용은 더 커지게 된다.

 

import re 

data = """
park 800905-1049118
kim  700905-1059119
"""

pat = re.compile("(\d{6})[-]\d{7}")
print(pat.sub("\g<1>-*******", data))

# 결과값:
# park 800905-*******
# kim  700905-*******

출처 : wikidocs.net/1642

 

 

정규표현식은 내용이 너무 많아서 다음 사이트를 참고하는 것이 좋겠다.

 

정규 표현식 시작하기 - wikidocs.net/4308

 

위키독스

온라인 책을 제작 공유하는 플랫폼 서비스

wikidocs.net

강력한 정규 표현식의 세계로 - wikidocs.net/4309

 

위키독스

온라인 책을 제작 공유하는 플랫폼 서비스

wikidocs.net

 

위의 블로그 내용을 잘 설명해준 동영상 - www.youtube.com/watch?v=dTDoTR0MXjU

 

댓글