
내가 생각했을때 문제에서 원하는부분
첫 번째 줄부터 네 개의 줄에 걸쳐 각 줄에 키보드의 키에 해당하는 10개의 문자가 주어집니다.
모든 문자는 서로 중복되지 않으며, ASCII 값이 33 이상 126 이하인 문자들만 주어집니다.
즉, 제어 문자(control character)가 아닌 출력 가능한 문자(printable character)로만 구성되어 있습니다.
다섯 번째 줄에 메모장에 무작위 순서로 입력된 문자열이 주어집니다.
정답은 유일하게 존재하며, 반드시 9개의 문자가 입력됨이 보장됩니다.
관우가 키보드를 내려친 위치에 있는 키의 문자를 출력해주세요.
내가 이 문제를 보고 생각해본 부분
입력값 읽기 및 저장 (BufferedReader)
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));: BufferedReader는 입력 스트림을 효율적으로 읽어들이는 데 사용된다.
콘솔 입력(System.in)을 통해 데이터를 가져온다.
char[][] keyboard = new char[4][10];: 키보드 배열은 4줄로 주어지고 각 줄에 10개의 문자가 있으니, char 타입의 4행 10열 2차원 배열로 선언한다.
for (int i = 0; i < 4; i++) { keyboard[i] = br.readLine().toCharArray(); }: 첫 4줄을 읽어서 keyboard 2차원 배열에 한 줄씩 문자 배열로 저장한다.
Set<Character> memoChars = new HashSet<>();: 메모장에 입력된 문자들은 순서가 무작위이다.
그리고 어떤 문자들이 있는지 포함 여부만 중요하기 때문에, 중복을 허용하지 않고 빠르게 찾을 수 있는 HashSet을 사용했다.
String memoInput = br.readLine(); for (char c : memoInput.toCharArray()) { memoChars.add(c); }: 다섯 번째 줄에서 메모장 입력 문자열을 읽어와서 각 문자를 memoChars에 추가한다.
8방향 탐색을 위한 방향 배열 (dr, dc)
int[] dr = {-1, -1, -1, 0, 0, 1, 1, 1};: dr 배열은 현재 위치 r (행)에서 이웃한 키의 행 변화량을 저장해요. 예를 들어 -1은 위쪽, 0은 같은 행, 1은 아래쪽을 의미한다.
int[] dc = {-1, 0, 1, -1, 1, -1, 0, 1};: dc 배열은 현재 위치 c (열)에서 이웃한 키의 열 변화량을 저장해요. -1은 왼쪽, 0은 같은 열, 1은 오른쪽을 의미한다.
dr과 dc를 함께 보면 (-1,-1)은 왼쪽 위 대각선, (-1,0)은 바로 위 등 8가지 방향을 모두 나타낼 수 있다.
샷건 맞은 키 찾기 (브루트 포스)
for (int r = 0; r < 4; r++) { for (int c = 0; c < 10; c++) { ... } }: 키보드의 모든 키를 하나씩 "샷건 맞은 중심 키"라고 가정하고 탐색할 거다.
이게 바로 '브루트 포스(Brute Force)' 알고리즘 방식이다.
Set<Character> currentKeys = new HashSet<>();: 현재 중심 키와 그 주변 키들을 담을 HashSet을 새로 만든다.
currentKeys.add(keyboard[r][c]);: 현재 (r, c) 위치의 키를 먼저 currentKeys에 추가한다.
이게 우리가 가정하는 "중심 키"이다.
for (int i = 0; i < 8; i++) { ... }: 이제 dr과 dc 배열을 이용해서 현재 키의 8방향 이웃 키들을 확인한다.
int nr = r + dr[i]; int nc = c + dc[i];: 이웃 키의 새로운 행(nr)과 열(nc) 좌표를 계산한다.
if (nr >= 0 && nr < 4 && nc >= 0 && nc < 10) { ... }: 계산된 이웃 키의 좌표가 실제 키보드 범위(0~3행, 0~9열)를 벗어나는지 확인한다.
벗어나면 키가 없는 거니까 무시한다.
currentKeys.add(keyboard[nr][nc]);: 범위 안에 있는 이웃 키라면 currentKeys에 추가한다.
if (currentKeys.size() == 9 && memoChars.containsAll(currentKeys)) { ... }: 이렇게 currentKeys에는 현재 중심 키와 모든 이웃 키들이 모여있다.
이 currentKeys의 크기가 9개이고 (가운데 + 8개 이웃),
memoChars (메모장에 입력된 문자들)가 currentKeys에 있는 모든 문자를 포함하고 있다면 (그리고 반대로도 동일),
이것은 관우가 (r, c) 위치의 키를 샷건으로 내리쳤다는 의미가 된다.
result = String.valueOf(keyboard[r][c]);: 정답을 찾았으니 현재 중심 키의 문자를 result에 저장하고, break문을 이용해 모든 반복을 즉시 종료한다.
결과 출력
System.out.println(result);: 최종적으로 찾아낸 정답 키 문자를 출력하면 끝이다.
br.close();:
BufferedReader 사용을 마쳤으니, close() 메서드를 호출하여 열려있는 입력 스트림을 닫고 자원을 해제한다.
코드로 구현
package baekjoon.baekjoon_32;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashSet;
import java.util.Set;
// 백준 32371번 문제
public class Main1279 {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
// 키보드 배열을 저장할 2차원 char 배열 (4행 10열)
char[][] keyboard = new char[4][10];
for (int i = 0; i < 4; i++) {
keyboard[i] = br.readLine().toCharArray();
}
// 메모장에 입력된 9개의 문자를 저장할 HashSet
Set<Character> memoChars = new HashSet<>();
String memoInput = br.readLine();
for (char c : memoInput.toCharArray()) {
memoChars.add(c);
}
// 8방향 탐색을 위한 dx, dy 배열 (상, 하, 좌, 우, 대각선 4방향)
// dx: -1, -1, -1, 0, 0, 1, 1, 1
// dy: -1, 0, 1, -1, 1, -1, 0, 1
int[] dr = {-1, -1, -1, 0, 0, 1, 1, 1}; // row 변화
int[] dc = {-1, 0, 1, -1, 1, -1, 0, 1}; // col 변화
String result = ""; // 결과를 저장할 변수
// 키보드 모든 위치를 순회하며 샷건 맞은 키를 찾음 (브루트 포스)
for (int r = 0; r < 4; r++) { // 0부터 3까지 4개의 행
for (int c = 0; c < 10; c++) { // 0부터 9까지 10개의 열
Set<Character> currentKeys = new HashSet<>();
currentKeys.add(keyboard[r][c]); // 현재 키(중심) 추가
// 8방향 탐색
for (int i = 0; i < 8; i++) {
int nr = r + dr[i]; // 이웃 키의 행
int nc = c + dc[i]; // 이웃 키의 열
// 키보드 범위를 벗어나지 않는지 확인
if (nr >= 0 && nr < 4 && nc >= 0 && nc < 10) {
currentKeys.add(keyboard[nr][nc]); // 이웃 키 추가
}
}
// 현재 탐색한 9개의 키 집합과 메모장 문자 집합을 비교
// 두 집합의 크기가 같고, 메모장 문자들이 현재 키 집합에 모두 포함되어 있다면 정답!
if (currentKeys.size() == 9 && memoChars.containsAll(currentKeys)) {
result = String.valueOf(keyboard[r][c]); // 현재 중심 키가 정답
break; // 정답을 찾았으므로 반복문 종료
}
}
if (!result.isEmpty()) { // 정답을 찾았으면 바깥 반복문도 종료
break;
}
}
System.out.println(result); // 결과 출력
br.close();
}
}
코드와 설명이 부족할수 있습니다. 코드를 보시고 문제가 있거나 코드 개선이 필요한 부분이 있다면 댓글로 말해주시면 감사한 마음으로 참고해 코드를 수정 하겠습니다.