[백준] 2186. 문자판
문제 링크
풀이 과정

위의 예시에서 문자열 CAT을 만족시키는 경로는 총 9가지가 가능합니다.

같은 칸을 여러 번 방문할 수 있기 때문에, 좌표 (2, 1)에 위치한 A를 이용해 문자열을 만드는 경로 ⑤와 경로 ⑦이 가능하게 됩니다. 따라서, 이 문제는 방문 처리를 하는 배열 없이 탐색을 해야 되는 문제입니다.
또한, 위 경로 ⑤와 ⑦에서, 좌표 (2, 1)을 포함하는 그 이후의 경로는 동일한 것을 알 수 있습니다. 따라서, 임의의 좌표 (x, y) 이후의 진행 경로가 이전에 가봤던 경로일 경우, 이를 다시 탐색하는 데에 걸리는 시간을 단축시키기 위해 메모이제이션 기법이 사용됩니다.
target = br.readLine().toCharArray();
dp = new int[N][M][target.length];
for (int[][] arr : dp) {
for (int[] arrr : arr) {
Arrays.fill(arrr, -1);
}
}
변수 target 은 맨 마지막으로 주어지는 영단어 입력을 의미합니다.
dp[i][j][k] 는 좌표 (i, j)에 영단어( target )의 k 번째 인덱스로 방문했을 시, 그 뒤로 만들어지는 경로들의 수를 의미합니다. 예를 들어, target 이 break 인 경우, dp[1][2][3] 은 좌표 (1, 2)에서 문자 a를 만나 만들 수 있는 경로들의 수를 나타냅니다.
for (int[][] arr : dp) {
for (int[] arrr : arr) {
Arrays.fill(arrr, -1);
}
}
각 좌표에서의 가능한 경로의 수가 0이 될 수 있기 때문에, 이와 구분되는 초기화가 필요했습니다. 위 코드는 3차원 배열의 모든 요소를 -1로 초기화 합니다.
for (int i = 0; i < N; i++) {
for (int j = 0; j < M; j++) {
if (map[i][j] == target[0]) {
answer += dfs(i, j, 0);
}
}
}
모든 좌표를 돌며, 시작 문자가 타겟 문자열의 첫 번째 문자와 맞다면 DFS 탐색을 실행합니다. 그리고 반환된 경로 가짓수를 answer 변수에 누적시킵니다.
static int dfs(int x, int y, int now) {
if (dp[x][y][now] != -1) return dp[x][y][now];
if (now == target.length - 1) return dp[x][y][now] = 1;
dp[x][y][now] = 0;
for (int d = 0; d < 4; d++) {
for (int k = 1; k <= K; k++) {
int nx = x + dx[d] * k, ny = y + dy[d] * k;
if (nx < 0 || nx >= N || ny < 0 || ny >= M) continue;
if (map[nx][ny] == target[now + 1]) dp[x][y][now] += dfs(nx, ny, now + 1);
}
}
return dp[x][y][now];
}
dfs 메소드 는 좌표 (x, y)의 now 번째 문자로 이어지는 가능한 경로의 수를 리턴합니다.
if (dp[x][y][now] != -1) return dp[x][y][now];: 처음에 초기화 한 -1 값과 다르다면, 좌표 (x, y) 에 위치한 문자 이후로 경로를 만들어 본 적이 있기 때문에, 그 값을 가져다 씁니다.if (now == target.length - 1) return dp[x][y][now] = 1;: 타겟 문자열을 만들었으므로 가능한 경로가 됩니다. 따라서, 경로가 하나 추가됬음을 의미하도록 1을 리턴시킵니다.- 이제 상하좌우 네 방향에 대해 1부터 K만큼 이동시켜 봅니다. 이동된 좌표에 다음 번에 와야할 문자가 위치한다면,
dfs 메소드를 호출해 이동된 좌표를 시작으로 가능한 경로를 만들어 봅니다. 또한, 가능한 경로의 가짓수를 의미하는 리턴 값을 dp 요소에 누적하여 저장해 줍니다.
코드
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.StringTokenizer;
public class Main {
static int N, M, K;
static char[][] map;
static int[][][] dp;
static boolean[][] visit;
static int[] dx = {-1, 1, 0, 0}, dy = {0, 0, -1, 1};
static char[] target;
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
N = Integer.parseInt(st.nextToken());
M = Integer.parseInt(st.nextToken());
K = Integer.parseInt(st.nextToken());
map = new char[N][M];
visit = new boolean[N][M];
int answer = 0;
for (int i = 0; i < N; i++) {
String s = br.readLine();
for (int j = 0; j < M; j++) {
map[i][j] = s.charAt(j);
}
}
target = br.readLine().toCharArray();
dp = new int[N][M][target.length];
for (int[][] arr : dp) {
for (int[] arrr : arr) {
Arrays.fill(arrr, -1);
}
}
for (int i = 0; i < N; i++) {
for (int j = 0; j < M; j++) {
if (map[i][j] == target[0]) {
answer += dfs(i, j, 0);
}
}
}
System.out.println(answer);
}
static int dfs(int x, int y, int now) {
if (dp[x][y][now] != -1) return dp[x][y][now];
if (now == target.length - 1) return dp[x][y][now] = 1;
dp[x][y][now] = 0;
for (int d = 0; d < 4; d++) {
for (int k = 1; k <= K; k++) {
int nx = x + dx[d] * k, ny = y + dy[d] * k;
if (nx < 0 || nx >= N || ny < 0 || ny >= M) continue;
if (map[nx][ny] == target[now + 1]) dp[x][y][now] += dfs(nx, ny, now + 1);
}
}
return dp[x][y][now];
}
}
댓글남기기