백준 조옮김

KIMYEONGJUN·2026년 1월 28일
post-thumbnail

문제

내가 생각했을때 문제에서 원하는부분

입력은 여러 개의 테스트 케이스로 이루어져 있다.
각 테스트 케이스의 첫째 줄에는 어떤 곡의 음의 순서가 주어진다.
음과 음은 하나 또는 그 이상의 공백으로 구분되어 있다.
다음 줄에는 조옮김해야 하는 값이 주어진다.
이 값이 양수일 경우 그 값만큼 위로 옮겨야 하고, 음수일 경우 아래로 옮겨야 한다.
입력의 마지막 줄에는 "***"이 주어진다.

각 테스트 케이스에 대해서, 조옮김한 결과를 출력한다.
출력하는 음은 문제 설명에 나와있는 12개 음에 나와있는 음이어야 한다.

내가 이 문제를 보고 생각해본 부분

SCALE_ARRAY (표준 음계 배열)
private static final String[] SCALE_ARRAY = {"A", "A#", "B", "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#"};
이 배열은 12음계의 표준적인 이름들을 순서대로 저장하고 있다. 
인덱스 0부터 11까지 각 음을 대표하는 문자열이 들어있다. 
최종적으로 결과를 출력할 때 이 배열을 사용한다. 
Bb가 아니라 A#으로 출력해야 하는 문제 조건 때문에 이 배열이 아주 중요하다.
SCALE_MAP (음계 인덱스 매핑 맵)
private static final HashMap<String, Integer> SCALE_MAP = new HashMap<>();
이 HashMap은 입력으로 들어올 수 있는 다양한 음계 이름(예: "A", "A#", "Bb", "B#", "Db" 등)을 해당 음계의 정규 인덱스(0부터 11까지)로 매핑하는 역할을 한다. 
예를 들어, "A"는 0, "A#"는 1, "Bb"도 1로 매핑된다.
initScaleMaps() 메서드
SCALE_ARRAY에 있는 표준 음계들을 SCALE_MAP에 추가한다. 
A -> 0, A# -> 1, ..., G# -> 11 이런 식이다.
그다음, 문제에서 언급했듯이 같은 음이지만 다르게 표기되는 경우들을 추가로 매핑한다.
Bb는 A#과 같으므로 1로 매핑.
Cb는 B와 같으므로 2로 매핑.
Db는 C#과 같으므로 4로 매핑.
Eb는 D#과 같으므로 6로 매핑.
Fb는 E와 같으므로 7로 매핑.
Gb는 F#과 같으므로 9로 매핑.
Ab는 G#과 같으므로 11로 매핑.
B#은 C와 같으므로 3으로 매핑.
E#은 F와 같으므로 8로 매핑.
이렇게 함으로써 어떤 형식의 음계가 입력되더라도 정확한 인덱스를 얻을 수 있게 된다.
main 메서드
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
BufferedReader를 사용해서 입력값을 효율적으로 읽어온다.
StringBuilder sb = new StringBuilder();
StringBuilder는 여러 줄의 출력 결과를 효율적으로 합쳐서 최종적으로 한 번에 출력하기 위해 사용된다.
문자열을 자주 변경하거나 추가할 때 + 연산보다 성능이 좋다.
initScaleMaps();
앞서 설명한 대로 음계 매핑을 먼저 초기화한다.
반복문 (while):
while ((line = br.readLine()) != null && !line.equals("***")): 입력에서 한 줄을 읽고, 읽은 줄이 null이 아니고 ***이 아니라면 반복한다. 
***은 입력의 끝을 나타내는 마커이다.
String[] notes = line.split("\\s+");: 첫 번째 줄에 있는 음들(예: "C# E Db G#")을 공백 기준으로 분리해서 notes 배열에 저장한다. 
\\s+는 하나 이상의 공백을 의미한다.
int transValue = Integer.parseInt(br.readLine());: 다음 줄에서 조옮김할 반음의 개수를 읽어와 정수로 변환한다.
음계 변환 및 조옮김 (for 루프):
for (int i = 0; i < notes.length; i++): notes 배열의 각 음에 대해 반복한다.
String note = notes[i];: 현재 처리할 음계 문자열을 가져온다.
int originalIndex = SCALE_MAP.get(note);: SCALE_MAP을 이용해서 현재 음계 문자열에 해당하는 인덱스를 가져온다. 
예를 들어, "C#"를 넣으면 4를 반환한다.
int transposedIndex = (originalIndex + transValue) % 12;: 조옮김을 수행하는 핵심 부분이다.
originalIndex에 transValue를 더해서 새로운 인덱스를 계산한다.
% 12는 12음계를 넘어가면 다시 처음으로 돌아오도록 하는 모듈러 연산이다. 
예를 들어, 인덱스 11 (G#)에서 1을 더하면 12가 되는데, % 12를 하면 0이 되어 A로 돌아오게 된다.
if (transposedIndex < 0) { transposedIndex += 12; }: 자바의 % 연산자는 피연산자 중 하나가 음수일 때 결과도 음수가 나올 수 있다 (예: -1 % 12는 -1). 
음수 인덱스는 없으므로, 결과가 음수가 되면 12를 더해서 양수로 만들어준다. 
예를 들어 -1이 나오면 +12를 해서 11이 되도록 한다.
sb.append(SCALE_ARRAY[transposedIndex]);: 계산된 transposedIndex를 이용해서 SCALE_ARRAY에서 해당하는 표준 음계 문자열을 가져와 StringBuilder에 추가한다.
if (i < notes.length - 1) { sb.append(" "); }: 현재 음이 마지막 음이 아니라면, 다음 음과의 구분을 위해 공백을 추가한다.
sb.append("\n");: 한 테스트 케이스의 모든 음계 처리가 끝나면, 다음 테스트 케이스와 구분을 위해 줄바꿈 문자를 추가한다.
System.out.print(sb.toString());: 모든 테스트 케이스의 처리가 끝난 후, StringBuilder에 저장된 모든 결과를 한 번에 콘솔에 출력한다.
br.close();: BufferedReader를 닫아서 자원을 해제한다.

코드로 구현

package baekjoon.baekjoon_32;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashMap;

// 백준 4732번 문제
public class Main1281 {
    // 12음계 표준 이름 배열 (출력용)
    private static final String[] SCALE_ARRAY = {"A", "A#", "B", "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#"};
    // 음계 이름을 인덱스로 매핑하기 위한 맵
    private static final HashMap<String, Integer> SCALE_MAP = new HashMap<>();

    // 스케일 맵 초기화
    private static void initScaleMaps() {
        // 표준 음계 매핑
        for (int i = 0; i < SCALE_ARRAY.length; i++) {
            SCALE_MAP.put(SCALE_ARRAY[i], i);
        }

        // 같은 음이지만 다른 표기법인 경우를 매핑 (모두 # 또는 내추럴 음으로 통일)
        SCALE_MAP.put("Bb", 1); // A#
        SCALE_MAP.put("Cb", 2); // B
        SCALE_MAP.put("Db", 4); // C#
        SCALE_MAP.put("Eb", 6); // D#
        SCALE_MAP.put("Fb", 7); // E
        SCALE_MAP.put("Gb", 9); // F#
        SCALE_MAP.put("Ab", 11); // G#

        SCALE_MAP.put("B#", 3); // C
        SCALE_MAP.put("E#", 8); // F
    }

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringBuilder sb = new StringBuilder();

        // 음계 맵 초기화
        initScaleMaps();

        String line;
        while ((line = br.readLine()) != null && !line.equals("***")) {
            String[] notes = line.split("\\s+"); // 음들을 공백 기준으로 분리
            int transValue = Integer.parseInt(br.readLine()); // 조옮김 값 읽기

            for (int i = 0; i < notes.length; i++) {
                String note = notes[i];
                int originalIndex = SCALE_MAP.get(note); // 현재 음의 인덱스 가져오기

                // 조옮김 값 적용 (양수, 음수 모두 처리 가능)
                int transposedIndex = (originalIndex + transValue) % 12;

                // 인덱스가 음수가 될 경우를 대비 (Java의 % 연산자는 음수를 그대로 반환할 수 있음)
                if (transposedIndex < 0) {
                    transposedIndex += 12;
                }

                sb.append(SCALE_ARRAY[transposedIndex]); // 조옮김된 음 추가
                if (i < notes.length - 1) {
                    sb.append(" "); // 음들 사이에 공백 추가
                }
            }
            sb.append("\n"); // 한 테스트 케이스 끝나면 개행
        }

        System.out.print(sb.toString()); // 결과 출력
        br.close();
    }
}

마무리

코드와 설명이 부족할수 있습니다. 코드를 보시고 문제가 있거나 코드 개선이 필요한 부분이 있다면 댓글로 말해주시면 감사한 마음으로 참고해 코드를 수정 하겠습니다.

profile
Junior backend developer

0개의 댓글