[Programmers] [1차] 다트 게임
[Programmers] [1차] 다트 게임

[Programmers] [1차] 다트 게임

Tags
Algorithm
Java
Published
October 19, 2023
Author
lkdcode

[1차] 다트 게임


문제 분석

자세한 내용은 위의 링크를 참조.
  1. 문자열 dartResult 가 주어진다.
  1. S, D, T 중 1개씩 총 3개를 포함한다.
      • S : 점수의 1승
      • D : 점수의 2승
      • T : 점수의 3승
  1. 0~10 까지 점수 중 1개씩 총 3개를 포함한다.
  1. # 중 1개씩 총 3개를 랜덤으로 포함한다.
      •  : 해당 점수 2배, 이전 점수 2배
      • # : 점수를 음수로 변경
  1. 얻은 점수를 리턴하라.
ex)
dartResult = "1S2D*3T"
1. 첫 번째 점수 : 점수 : 1, 보너스 : S, 옵션 : 없음
2. 두 번째 점수 : 점수 : 2, 보너스 : D, 옵션 : *
3. 세 번째 점수 : 점수 : 3, 보너스 : T, 옵션 : 없음
dartResult = "1S*2T*3S"
1. 첫 번째 점수 : 점수 : 1, 보너스 : S, 옵션 :2. 두 번째 점수 : 점수 : 2, 보너스 : T, 옵션 :3. 세 번째 점수 : 점수 : 3, 보너스 : S, 옵션 : 없음
입출력 예제
notion image

풀이 과정

코딩 테스트에서 흔히 볼 수 있는 첫 번째 문제 유형이다.
보통 첫 번째 유형으로 문자열 가공 문제가 많이 제출되는 것 같다.
문자열 가공 로직이 중요하다.
어떻게 문자열을 요리할 것인가? 그것이 나의 접근 방식이다.

🎯 문자열 가공

해당 문제는 항상 등장하는 보너스 및 점수 를 기준으로 문자열을 나눌 수 있을 것 같다.
옵션 은 등장할 수도, 안 할 수도 있다. 그래서 옵션 을 기준으로는 나눌 수 없다.
보너스 와 점수 중 어느 문자를 기준으로 문자열을 나눌 것인가?
점수 의 length 는 1 아니면 2 다. (0점 ~ 10점)
보너스 는 항상 SDT 중 하나일 것이다.
length 기준이 S, D, T 보다 번거롭다.
보너스 를 기준으로 문자열을 나눈 뒤, 점수 를 계산하고 옵션 을 계산한다.
문자열을 담을 배열을 선언한다.
총 3번의 기회이므로 사이즈는 3
점수보너스옵션 총 3가지의 배열 선언
int[] point = new int[3]; // 점수를 담을 배열 String[] bonus = new String[3]; // 보너스를 담을 배열 String[] option = new String[3]; // 옵션을 담을 배열

🎯 S, D, T 로 문자열을 나누기

주어질 dartResult 이 어떤 문자열(값)을 가지고 있을지 모르지만,
점수보너스 는 확실히 있다.
그 중에서도 보너스(S, D, T) 를 기준으로 문자열을 자르자.
int index = 0; // 배열 인덱스 String inputPoint = ""; // 추가할 점수 for (int i = 0; i < dartResult.length(); i++) { char check = dartResult.charAt(i); // 하나씩 잘라서, if (check == 'S' || check == 'D' || check == 'T') { // S, D, T 중 하나라면 point[index] = Integer.parseInt(inputPoint.replaceAll("[#*]", "")); // point에 점수 추가! bonus[index++] = String.valueOf(check); // 보너스에 S, D, T 중 하나 추가! , 배열 인덱스 증가 inputPoint = ""; // 추가할 점수 문자열 초기화 continue; // 아래 로직 스킵 } inputPoint += check; // S, D, T 가 아니라면 점수다. }
charAt() 으로 잘라낸 문자가 만약 S, D, T 중 하나라면 각 배열에 추가해준다.
그게 아니라면 String inputPoint 에 더해주게 되는데
S, D, T 가 아니라면 점수거나 옵션일테고 inputPoint 에 값이 할당된다.
예시로 "1S2D*3T" 가 주어진다면,
처음 S 가 등장하는 시점에 String inputPoint 는 "1" 이 될 것이다.
다시 if 문 로직으로 돌아가서,
점수만 체크하기 위해 inputPoint.replaceAll() 로 옵션들을 제거해준뒤
int point[] 배열에 추가해준다.
String bonus[] 배열도 마찬가지로 추가해준다. (.charAt() 자체가 S, D, T 중 하나)
String inputPoint 의 문자열을 초기화해준뒤 continue 를 실행해 다음 로직을 스킵해준다.

🎯 가지고 있는 배열로 먼저 계산

점수와 보너스를 얻었기 때문에 먼저 계산해보자.
첫 번째 점수 = point[0] * bonus[0]
두 번째 점수 = point[1] * bonus[1]
세 번째 점수 = point[2] * bonus[2]
만약,
bonus[] == 'S' 라면 point[] * 1 (point[]의 1승)
bonus[] == 'D' 라면 point[] * point[] (point[]의 2승)
bonus[] == 'T' 라면 point[] * point[] * point[] (point[]의 3승)
for (int i = 0; i < point.length; i++) { if (bonus[i].equals("D")) { point[i] = point[i] * point[i]; } if (bonus[i].equals("T")) { point[i] = point[i] * point[i] * point[i]; } }
S 1승은 할 필요 없다.
D, T 만 계산해주자.

🎯 옵션을 체크해보자

보너스는 항상 등장, length 는 1 이다. (SDT)
점수도 항상 등장, length 1 or 2 다. (0 ~ 10)
옵션은 등장할 수도, 안 할수도..
1S1D1T 가 주어질 수도, 10S*10D*10T* 가 주어질 수도 있다.
특정 위치에 고정적으로 옵션이 등장하지 않으므로
문자열의 최소 렝스와 최대 렝스 를 따지면 계산이 조금 번거로워진다.
첫 번째 옵션과 마지막 옵션을 기준으로 체크하고 그 외에 위치는 두 번째 옵션으로 본다.
for (int i = 0; i < dartResult.length(); i++) { char check = dartResult.charAt(i); // 하나씩 문자로 잘라서 if (check == '*' || check == '#') { // 옵션이라면, if (i < 3) option[0] = String.valueOf(check); // 첫번째 옵션 등장 규칙 else if (i == dartResult.length() - 1) option[2] = String.valueOf(check); // 마지막 옵션 등장 규칙 else option[1] = String.valueOf(check); // 그 외 등장은 항상 두 번째 옵션 } }
옵션이 아예 없는 최소 length 든,
10점이면서 옵션 이 전부 등장하는 최대 length 든
(ex.1S1D1T10S*10D*10T*)
첫 번째 옵션 등장 위치는 항상 3미만이다.
세 번째 옵션 등장 위치는 항상 마지막 렝스다.
두 번째 옵션 등장 위치는 위의 둘이 아니면 맞다.

🎯 옵션을 계산하자

옵션 이 비어있다면 스킵,
그게 아니라면 주어진 문제에 따라 계산해주자.
# 은 점수를 음수로 바꿔주는 것.
  •  은 해당 점수를 2배로, 그 앞의 점수도 2배로.
for (int i = 0; i < option.length; i++) { if (option[i] == null) continue; if (option[i].equals("#")) { point[i] -= point[i] * 2; } if (option[i].equals("*")) { point[i] += point[i]; int preIndex = i - 1; if (preIndex >= 0) point[preIndex] += point[preIndex]; } }

나의 생각

문자열 가공 문제는 항상 여러 로직들을 순차적으로 실행시키는 문제들이 많은 것 같다.
얼만큼 자료 구조에 잘 담고 있으며 너가 생각하는 로직이 얼마나 단순하고 어떤 효율이 보여주는가? 를 묻는 느낌이 강하다.
문자열 가공 문제는 꼬여있는 문제들을 하나씩 천천히 풀다보면 정답을 얻을 수 있는 것 같다.

코드

import java.util.*; class Solution { public int solution(String dartResult) { int answer = 0; int[] point = new int[3]; String[] bonus = new String[3]; String[] option = new String[3]; int index = 0; String inputPoint = ""; for (int i = 0; i < dartResult.length(); i++) { char check = dartResult.charAt(i); if (check == 'S' || check == 'D' || check == 'T') { point[index] = Integer.parseInt(inputPoint.replaceAll("[#*]", "")); bonus[index++] = String.valueOf(check); inputPoint = ""; continue; } inputPoint += check; } for (int i = 0; i < point.length; i++) { if (bonus[i].equals("D")) { point[i] = point[i] * point[i]; } if (bonus[i].equals("T")) { point[i] = point[i] * point[i] * point[i]; } } for (int i = 0; i < dartResult.length(); i++) { char check = dartResult.charAt(i); if (check == '*' || check == '#') { if (i < 3) option[0] = String.valueOf(check); else if (i == dartResult.length() - 1) option[2] = String.valueOf(check); else option[1] = String.valueOf(check); } } /////////////////////////////////////// // for (int i : point) { // System.out.print(i + " "); // } /////////////////////////////////////// // for (String s : option) { // System.out.println(s); // } /////////////////////////////////////// for (int i = 0; i < option.length; i++) { if (option[i] == null) continue; if (option[i].equals("#")) { point[i] -= point[i] * 2; } if (option[i].equals("*")) { point[i] += point[i]; int preIndex = i - 1; if (preIndex >= 0) point[preIndex] += point[preIndex]; } } /////////////////////////////////////// // System.out.println(); // for (int i : point) { // System.out.print(i + " "); // } /////////////////////////////////////// return Arrays.stream(point).sum(); } }
notion image
notion image