본문 바로가기
PS/BOJ

[자바] 백준 2418 - 단어 격자 (java)

by Nahwasa 2023. 12. 8.

문제 : boj2418

 

 

필요 알고리즘

  • DP (동적 계획법)
    • BFS 같은걸론 시간초과가 나게된다. 동적 계획법으로 풀 수 있는 문제이다.

※ 제 코드에서 왜 main 함수에 로직을 직접 작성하지 않았는지, 왜 Scanner를 쓰지 않고 BufferedReader를 사용했는지 등에 대해서는 '자바로 백준 풀 때의 팁 및 주의점' 글을 참고해주세요. 백준을 자바로 풀어보려고 시작하시는 분이나, 백준에서 자바로 풀 때의 팁을 원하시는 분들도 보시는걸 추천드립니다.

 

 

풀이

  '각 칸은 중복되게 사용해도 된다.' 부분이 문제인데, 이 부분때문에 BFS나 DFS로 진행 시 중복체크를 못하므로 시간복잡도가 저 멀리 가버리게 된다. 게다가 경우의 수의 경우 최대 10^18 이라고 했으므로, 따로 시간복잡도를 계산해보지 않아도 10^18번 찾는건 불가함을 알 수 있다.

 

  DP로 접근하면 생각보다 쉽게 풀 수 있다.

dp[a][b][c]를 (a, b) 칸에서 입력된 문자 중 c번 인덱스 문자까지 읽는 경우의 수라고 정의해보자.

이 경우 dp[a][b][c]는 c번 인덱스 문자가 'X'이고, c-1번 인덱스 문자가 'Y'라고 하면 (a,b)칸에 'X'가 존재할 경우 그 주변 8칸에 'Y'가 존재할 경우 그 경우의수를 더해주면 된다. 예를들어 (a-1, b-1)이 'Y'였다면 dp[a][b][c] += dp[a-1][b-1][c-1]이 된다.

 

  예제 입력 1의 경우를 보자.

3 4 5
ERAT
ATSR
AUTU
TARTU

dp[a][b][0] 부터 dp[a][b][5]를 dp[a][b] 까지만 나타내보면 다음과 같다. 각 칸의 문자는 입력으로 줄어졌던 격자의 문자이고, 숫자는 dp 배열에 담긴 경우의 수를 나타낸다. 다만 회색 숫자는 바로 직전(c-1)의 숫자를 알아보기 편하게 적어둔 것이다.

 

 

코드 : github

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.*;

public class Main {
    static BufferedReader br = new BufferedReader(new InputStreamReader(System.in), 1 << 16);

    public static void main(String[] args) throws Exception {
        new Main().solution();
    }

    public void solution() throws Exception {
        StringTokenizer st = new StringTokenizer(br.readLine());
        int r = Integer.parseInt(st.nextToken());
        int c = Integer.parseInt(st.nextToken());
        int len = Integer.parseInt(st.nextToken());

        char[][] arr = new char[r][c];
        for (int i = 0; i < r; i++) {
            String cur = br.readLine();
            for (int j = 0; j < c; j++) {
                arr[i][j] = cur.charAt(j);
            }
        }

        String word = br.readLine();
        long[][][] dp = new long[r][c][len];
        for (int i = 0; i < r; i++) {
            for (int j = 0; j < c; j++) {
                if (arr[i][j] == word.charAt(0))
                    dp[i][j][0] = 1;
            }
        }

        long answer = 0;
        for (int k = 1; k < len; k++) {
            char cur = word.charAt(k);
            char bf = word.charAt(k-1);

            for (int i = 0; i < r; i++) {
                for (int j = 0; j < c; j++) {
                    if (arr[i][j] != cur) continue;

                    for (int dr = -1; dr <= 1; dr++) {
                        for (int dc = -1; dc <= 1; dc++) {
                            if (dr==0 && dc==0) continue;
                            int nr = i+dr;
                            int nc = j+dc;
                            if (nr<0||nr>=r||nc<0||nc>=c||arr[nr][nc]!=bf) continue;

                            dp[i][j][k] += dp[nr][nc][k-1];
                        }
                    }

                    if (k == len-1) answer += dp[i][j][k];
                }
            }
        }

        System.out.println(answer);
    }
}