내가 생각했을때 문제에서 원하는부분
첫째 줄에 100 이하의 테스트 케이스의 개수가 주어진다. 그리고 각 테스트 케이스마다
대문자로만 이루어진 10만자 이하의 문자열 M이 한 줄에 주어진다.
이 문자열은 검사해야할 메시지다.
테스트 케이스마다
메시지 M이 진짜 메시지면 “OK”를, 가짜 메시지면 “FAKE”를 한 줄에 출력한다.
내가 이 문제를 보고 생각해본 부분
입출력 효율성을 위해 BufferedReader와 StringBuilder를 사용한다.
checkMessage 메서드: 이 메서드가 실제 메시지의 변형 규칙 준수 여부를 검사하는 핵심 로직을 담고 있다.
주어진 메시지가 "OK"인지 "FAKE"인지 판별하여 문자열로 반환한다.
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
사용자로부터 입력(콘솔, 파일 등)을 받기 위한 준비 과정이다.
BufferedReader는 InputStreamReader를 통해 시스템 표준 입력 스트림 (System.in)을 읽어들이며, 대량의 입력을 빠르게 처리하는 데 유용하다.
StringBuilder sb = new StringBuilder();
결과를 효율적으로 저장하기 위한 객체이다.
String 객체는 불변(immutable)이라 문자열을 계속 합치면 새로운 객체가 생성되어 메모리 사용과 성능에 비효율적일 수 있다.
StringBuilder는 가변(mutable)이라 문자열을 추가하거나 변경해도 성능 저하가 적다.
여기서는 각 테스트 케이스의 결과를 모아두었다가 한 번에 출력하는 용도로 사용된다.
int T = Integer.parseInt(br.readLine());
첫 번째 줄에서 테스트 케이스의 개수 T를 읽어온다.
br.readLine()은 문자열을 반환하므로, Integer.parseInt()를 사용하여 정수로 변환한다.
for(int t = 0; t < T; t++) { ... }
T개의 테스트 케이스를 각각 처리하기 위한 반복문이다.
String message = br.readLine();
각 반복마다 한 줄씩 메시지 문자열 M을 읽어 message 변수에 저장한다.
sb.append(checkMessage(message)).append("\n");
checkMessage 메서드를 호출하여 현재 message가 진짜 메시지인지("OK") 가짜 메시지인지("FAKE")를 판별하게 한다.
checkMessage 메서드가 반환한 문자열("OK" 또는 "FAKE")을 StringBuilder sb에 추가하고, 각 결과마다 줄바꿈 문자("\n")를 추가하여 다음 테스트 케이스의 결과와 구분한다.
System.out.print(sb.toString());
모든 테스트 케이스의 처리가 끝난 후, StringBuilder sb에 저장된 모든 결과를 한꺼번에 화면에 출력한다.
br.close();: BufferedReader 사용이 끝났으므로, 열린 자원을 닫아준다.
checkMessage 메서드 설명
int[] charCount = new int[26];
charCount 배열은 0으로 초기화된 26개의 정수 칸을 가진다.
각 칸은 'A'부터 'Z'까지의 각 알파벳 문자가 원래 메시지에서 몇 번째로 등장했는지를 기록하는 데 사용된다.
예를 들어, charCount[0]은 'A'의 등장 횟수를, charCount[1]은 'B'의 등장 횟수를 의미한다.
for(int i = 0; i < message.length(); i++) { ... }
이 for 반복문은 입력으로 받은 message 문자열의 첫 번째 문자부터 마지막 문자까지 순서대로 하나씩 살펴보는 역할을 한다.
변수 i는 현재 확인하고 있는 문자의 인덱스(위치)를 나타낸다.
char currentChar = message.charAt(i);
현재 인덱스 i에 해당하는 문자를 currentChar 변수에 저장한다.
int charIndex = currentChar - 'A';
문자(char)를 정수 인덱스로 변환하는 방법이다.
'A'의 아스키(ASCII) 값은 65이고, 'B'는 66 등 순차적으로 증가한다.
따라서 currentChar에서 'A'의 아스키 값을 빼면, 'A'는 0, 'B'는 1과 같이 0부터 25 사이의 숫자가 되어 charCount 배열의 인덱스로 활용할 수 있다.
charCount[charIndex]++;
현재 문자에 해당하는 charCount 배열의 값을 1 증가시킨다.
이 값은 현재 문자가 (원본 메시지 기준으로) 몇 번째로 등장했는지를 나타낸다.
if(charCount[charIndex] % 3 == 0) { ... }
가장 중요한 조건문이다.
현재 문자의 등장 횟수가 3의 배수(3, 6, 9...)인 경우, 문제의 규칙에 따라 바로 다음 위치에 동일한 문자가 하나 더 삽입되어야 한다.
이 조건문은 바로 이 삽입 규칙이 제대로 지켜졌는지 확인한다.
if(i + 1 >= message.length()) { return "FAKE"; }
현재 인덱스 i의 다음 칸(i+1)을 확인해야 하는데, i+1이 이미 메시지의 길이를 넘어서는 경우이다.
즉, 현재 문자가 문자열의 마지막 문자이거나 그 직전 문자라서 다음에 삽입될 문자가 올 공간이 없다는 뜻이다.
이는 규칙이 깨진 것이므로 즉시 "FAKE"를 반환한다.
if(message.charAt(i + 1) != currentChar) { return "FAKE"; }
i+1 위치에 문자가 존재하기는 하지만, 그 문자가 현재 currentChar와 다른 경우이다.
규칙에 따르면 currentChar와 동일한 문자가 삽입되어야 하는데 다른 문자가 왔다면, 이는 규칙 위반이므로 즉시 "FAKE"를 반환한다.
i++;
위의 두 if 조건을 모두 통과했다는 것은, i+1 위치에 currentChar와 동일한 문자가 규칙에 따라 올바르게 삽입되었다는 의미이다.
이때 중요한 점은 이 삽입된 문자는 원본 메시지의 일부가 아니라는 것이다.
따라서 다음 반복에서 이 삽입된 문자를 다시 charCount에 반영하거나 검사할 필요가 없다.
i를 1 증가시켜 i+1 위치의 삽입된 문자를 건너뛰고, 그 다음(i+2) 문자로 넘어가도록 한다.
이렇게 함으로써 불필요한 중복 검사를 피하고, charCount가 항상 원본 메시지의 문자 등장 횟수를 정확하게 반영하도록 유지한다.
return "OK";
for 반복문이 끝까지 실행되는 동안, 위의 어떤 if 문에서도 "FAKE"를 반환하지 않았다면, 이는 메시지가 모든 변형 규칙을 완벽하게 준수했다는 뜻이다.
따라서 최종적으로 "OK"를 반환한다.
코드로 구현
package baekjoon.baekjoon_30;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
// 백준 9324번 문제
public class Main1155 {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringBuilder sb = new StringBuilder();
int T = Integer.parseInt(br.readLine()); // 테스트 케이스의 개수
for(int t = 0; t < T; t++) {
String message = br.readLine(); // 검사할 메시지
sb.append(checkMessage(message)).append("\n"); // 메시지 검사 후 결과 저장
}
System.out.print(sb.toString()); // 결과 한 번에 출력
br.close();
}
// 주어진 메시지가 진짜 메시지인지(OK) 가짜 메시지인지(FAKE) 판별하는 메서드
public static String checkMessage(String message) {
// 알파벳 대문자 ('A'부터 'Z'까지 26개)의 등장 횟수를 저장하는 배열
int[] charCount = new int[26];
// 메시지 문자열을 처음부터 끝까지 순회합니다.
for(int i = 0; i < message.length(); i++) {
char currentChar = message.charAt(i); // 현재 문자를 가져옵니다.
int charIndex = currentChar - 'A'; // 'A'를 0으로 하는 인덱스로 변환합니다. (예: 'A' -> 0, 'B' -> 1)
charCount[charIndex]++; // 현재 문자의 등장 횟수를 1 증가시킵니다.
// 만약 현재 문자의 등장 횟수가 3의 배수라면 (3번째, 6번째, 9번째 등)
if(charCount[charIndex] % 3 == 0) {
// 바로 다음 인덱스(i+1)에 삽입된 동일한 문자가 와야 합니다.
// 1. 문자열의 끝에 도달했는데 삽입될 문자가 없는 경우
if(i + 1 >= message.length()) {
return "FAKE"; // 규칙 위반: 삽입될 문자가 없습니다.
}
// 2. 다음 문자가 현재 문자와 다른 경우
if(message.charAt(i + 1) != currentChar) {
return "FAKE"; // 규칙 위반: 삽입될 문자가 다릅니다.
}
// 규칙에 따라 올바르게 동일한 문자가 삽입되었다면,
// 이 삽입된 문자는 다음 순회에서 다시 검사할 필요가 없으므로 i를 1 증가시켜 건너뜁니다.
i++;
}
}
// 모든 문자를 성공적으로 검사했고 규칙 위반이 없었다면 진짜 메시지입니다.
return "OK";
}
}
코드와 설명이 부족할수 있습니다. 코드를 보시고 문제가 있거나 코드 개선이 필요한 부분이 있다면 댓글로 말해주시면 감사한 마음으로 참고해 코드를 수정 하겠습니다.