storage
와 문자열 배열 requests
가 주어진다.requests
의 첫 글자는 제거해야 할 알파벳이며, 해당 알파벳이:requests
를 처리한 후 남은 문자 개수를 반환한다.💡 반례:
핵심 아이디어:
connOut
)을 사용하여 중복 검사를 피함.이 방법으로 내부 영역이 외부와 연결된 것처럼 판단되는 반례를 해결함. ✅
N
, M
: 행과 열의 크기 저장map
: storage
를 2차원 문자 배열로 변환deleted
: 해당 칸이 제거되었는지 여부를 저장connOut
: 해당 칸이 외부와 연결되었는지 여부를 저장dir
: 4방향 탐색을 위한 배열connOut
)를 외부와 연결됨(true)으로 설정map
을 순회하며 각 알파벳의 좌표를 pointMap
에 저장requests
를 순회하며:delete
)deleted
배열에 true
로 표시하여 제거 처리deleted
가 false
인 칸의 개수를 세어 반환import java.util.*;
class Solution {
int N, M; // 행과 열의 크기
char[][] map; // 2차원 문자 배열로 저장
Map<Character, Set<String>> pointMap = new HashMap<>();
boolean[][] deleted; // 해당 좌표가 제거되었는지 여부
boolean[][] connOut; // 외부와 연결 여부를 저장
int[][] dir = {{-1, 0, 1, 0}, {0, 1, 0, -1}}; // 상, 우, 하, 좌 탐색
public int solution(String[] storage, String[] requests) {
// ✅ 전역 변수 초기화
N = storage.length;
M = storage[0].length();
deleted = new boolean[N][M];
connOut = new boolean[N][M];
map = new char[N][M];
// ✅ 외부와 연결된 가장자리 초기화
for (int i = 0; i < N; i++) {
connOut[i][0] = true;
connOut[i][M - 1] = true;
}
for (int i = 0; i < M; i++) {
connOut[0][i] = true;
connOut[N - 1][i] = true;
}
// ✅ Map에 알파벳 좌표 저장
for (int i = 0; i < N; i++) {
for (int j = 0; j < M; j++) {
map[i][j] = storage[i].charAt(j);
pointMap.computeIfAbsent(map[i][j], k -> new HashSet<>()).add(i + "," + j);
}
}
// ✅ requests 탐색
for (String request : requests) {
char alpha = request.charAt(0);
if (!pointMap.containsKey(alpha)) continue; // 알파벳이 없으면 건너뜀
Set<String> set = pointMap.get(alpha);
if (request.length() == 1) { // ✅ 단일 문자 요청 (외부와 연결된 것만 제거)
List<int[]> deletedList = new ArrayList<>();
for (String str : set) {
String[] splits = str.split(",");
int x = Integer.parseInt(splits[0]);
int y = Integer.parseInt(splits[1]);
if (connOut[x][y]) deletedList.add(new int[]{x, y});
}
for (int[] cur : deletedList) {
delete(cur[0], cur[1]); // ✅ 삭제 및 외부 연결 처리
set.remove(cur[0] + "," + cur[1]);
}
} else { // ✅ 문자열 요청 (해당 알파벳 모두 제거)
for (String str : set) {
String[] splits = str.split(",");
int x = Integer.parseInt(splits[0]);
int y = Integer.parseInt(splits[1]);
delete(x, y); // ✅ 해당 좌표 제거
}
set.clear(); // ✅ 전체 제거했으므로 Set 비움
}
}
// ✅ 남은 문자 개수 세기
int answer = 0;
for (int i = 0; i < N; i++) {
for (int j = 0; j < M; j++) {
if (!deleted[i][j]) answer++;
}
}
return answer;
}
// ✅ 삭제 메서드
private void delete(int x, int y) {
if (connOut[x][y]) { // 외부와 연결된 경우
for (int i = 0; i < 4; i++) {
int dx = x + dir[0][i];
int dy = y + dir[1][i];
if (dx < 0 || dx >= N || dy < 0 || dy >= M) continue;
if (deleted[dx][dy] && !connOut[dx][dy]) { // ✅ 삭제되었지만 외부와 연결되지 않은 경우
connOut[dx][dy] = true; // ✅ 외부와 연결 표시
delete(dx, dy); // ✅ 재귀적으로 외부와 연결됨 처리
} else {
connOut[dx][dy] = true; // ✅ 바로 외부와 연결됨 표시
}
}
}
deleted[x][y] = true; // ✅ 현재 좌표 제거 처리
}
}
외부와 연결 여부(connOut
)
알파벳 요청 처리
삭제 메서드(delete
)
deleted
로 표시하여 제거 처리.시간복잡도:
requests
가 최대 2,500개이고 delete()
가 각 좌표당 최대 한 번만 호출되므로 메모리 사용:
deleted
와 connOut
은 2차원 배열로 고정 크기 사용 String[] storage = {"AAAB", "ABBB", "ABAB", "BBBB"};
String[] requests = {"A", "B"};
int result = solution(storage, requests);
System.out.println(result); // 0 (모두 제거됨)
String[] storage = {"AAA", "ABA", "AAA"};
String[] requests = {"A"};
int result = solution(storage, requests);
System.out.println(result); // 중앙 'A'는 남아있음
String[] storage = {"AAA", "ABB", "ABB"};
String[] requests = {"A"};
int result = solution(storage, requests);
System.out.println(result); // 외곽 'A'가 제거되며 내부도 외부와 연결되어 제거됨
N = 50
, M = 50
, requests = 2,500
connOut
)를 기록하여 중복 검사 제거 ➡ 최종적으로 효율적이고 정확하게 문제 해결! ✅🚀
connOut
)를 사용하여 중복 검사 제거