[백준 1256] 사전(Java)

최민길(Gale)·2023년 2월 19일
1

알고리즘

목록 보기
39/172

문제 링크 : https://www.acmicpc.net/problem/1256

이 문제는 dp를 이용하여 풀 수 있습니다. 이 문제의 경우 크게 2가지 부분에서 아이디어가 필요합니다.

  1. dp 점화식은 어떻게 세울 것인가
  2. dp 점화식과 문자열과의 상관관계는 어떤 식으로 처리해야 하는가

우선 1번의 경우 dp 점화식을 문자열의 개수로 설정합니다. 즉 dp[n][m] : a가 n개이고, z가 m개인 문자열의 개수로 설정합니다. 그 이유는 문제에서 K의 범위를 초과했을 경우 -1을 출력해야 하기 때문에 문자열 개수에 대한 정보를 가지고 있어야 하며, 뒤이어 설명드릴 2번 방식을 문자열 개수에 대한 정보로 처리할 수 있기 때문입니다.

2번의 경우는 케이스를 나눠서 확인할 수 있습니다. 만약 a 또는 z의 개수가 0일 경우 반대되는 문자를 끝까지 추가하면 됩니다. 하지만 둘 다 0이 아닌 경우는 k과 직전 dp값을 비교하는 방식으로 진행합니다.

만약 a가 하나 없는 dp, 즉 dp[n-1][m]보다 k가 작을 때를 생각해봅시다. dp[n-1][m]의 경우 a가 n-1개, z가 m개 존재하는 문자열의 개수를 의미하고 k는 순서를 의미합니다. 따라서 dp[n-1][m]의 값보다 k가 작을 경우는 앞쪽에서 수를 선택해서 문자열을 추가하는 것이므로 정렬된 값을 출력하기 때문에 a가 추가되어야 합니다. 마찬가지로 k가 더 크다면 z를 출력합니다.

이 때 z를 추가할 경우, 즉 dp[n-1][m]이 k보다 작을 경우 다음 수를 추가할 때 그 이후의 dp값들은 모두 k보다 작게 된다는 문제점이 있습니다. 이는 k값을 해당 값만큼 빼는 방식으로 k값의 크기를 조정해주면 뒤의 수들을 정상적으로 추가할 수 있습니다.

다음은 코드입니다.

import java.util.*;
import java.io.*;

public class Main{
    static int N,M,K;
    static StringBuilder sb = new StringBuilder();
    static int[][] dp = new int[101][101];

    static int init(int n, int m){
        // dp[n][m] : a가 n개이고, z가 m개인 문자열의 개수

        // 초기값 세팅
        // 두 값 중 어느 하나라도 0이면 개수는 1개밖에 만들지 못함
        if(n==0 || m==0){
            dp[n][m] = 1;
            return dp[n][m];
        }

        // dp에 값이 없다면 점화식으로 : dp[n-1][m] + dp[n][m-1]
        // dp[n][m]의 개수는 a가 하나 없는 문자열의 개수와 z가 하나 없는 문자열의 개수의 합과 같음
        else if(dp[n][m]==0){
            // 여기서 K값이 10억 이하이므로 10억 초과인 값이 점화식으로 나오게 된다면 -1로 처리해야 할 부분까지 처리하는 것이므로 10억으로 처리
            dp[n][m] = Math.min(init(n-1,m) + init(n,m-1), 1000000001);
            return dp[n][m];
        }

        // 값이 존재한다면 해당값 출력
        else return dp[n][m];
    }

    static void setString(int n, int m, int k){
        // 만약 a의 개수가 0일 경우 z만 이어서 추가
        if(n==0){
            for(int i=0;i<m;i++) sb.append("z");
            return;
        }

        // 만약 z의 개수가 0일 경우 a만 이어서 추가
        if(m==0){
            for(int i=0;i<n;i++) sb.append("a");
            return;
        }

        // 둘 다 살아있을 경우 맨 앞에서부터 문자열 추가
        // 바로 직전 dp의 값과 k값과 비교했을 때
        // 만약 a가 하나 없는 dp값보다 k가 작다면
        // k의 경우 순서를 의미하기 때문에 추가해야 할 문자는 a가 됨
        // 반대로 k가 크다면 z를 추가하는 식으로 진행
        // 이 때 z를 추가할 경우 k값에서 직전 dp값을 빼주어 다음 문자열을 추가할 때의 순서값을 보정
        if(k > dp[n-1][m]){
            sb.append("z");
            setString(n,m-1,k-dp[n-1][m]);
        }

        else{
            sb.append("a");
            setString(n-1,m,k);
        }
    }

    public static void main(String[] args) throws Exception{
        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());

        // dp 배열 값 추가
        init(N,M);

        // K가 문자열 개수보다 클 경우 -1 출력
        if(dp[N][M] < K) System.out.println(-1);

        // 아닐 경우 : 문자열 생성
        else{
            setString(N,M,K);
            System.out.println(sb);
        }

    }
}

profile
저는 상황에 맞는 최적의 솔루션을 깊고 정확한 개념의 이해를 통한 다양한 방식으로 해결해오면서 지난 3년 동안 신규 서비스를 20만 회원 서비스로 성장시킨 Software Developer 최민길입니다.

0개의 댓글