[백준] 1339번 단어 수학 Java 풀이

PROBLEM

https://www.acmicpc.net/problem/1339

SOLUTION

문제 지문에서 단어의 개수, 수의 길이를 고려했을 때 브루트포스 알고리즘으로 접근해도 괜찮을 것 같다는 생각을 했다. 따라서, 각 알파벳 별로 숫자를 부여하는 모든 경우의 수를 정하고 최대값을 찾는다.

각 알파벳은 0~9 사이 숫자에 대응되므로 0~9까지의 순열을 구해서 각 경우의 수일 때 값을 계산한다.

시간 제한은 2초이지만 java라서 시간을 더 준건지, 2792ms로 아슬아슬하게 통과한 듯 싶다.

SOURCE CODE (JAVA)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
import java.io.*;
import java.util.*;

public class Main {

  static int N;
  static int[] alphabetCount;
  static String[] words;
  static long max;

  public static void main(String args[]) throws Exception {
    BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

    N = Integer.parseInt(br.readLine());    // 단어의 개수

    int count = 0;                  // 순열의 크기를 결정하기 위해 등장한 알바펫의 종류를 센다.
    alphabetCount = new int[26];    // 등장했던 알파벳들에 대해서 번호를 부여하기 위해 개수를 센다.
    words = new String[N];          // 주어지는 단어들

    for (int i=0; i<N; i++) {
      words[i] = br.readLine();

      for (int j=0; j<words[i].length(); j++) {
        int alpha = words[i].charAt(j) - 'A';
        if (alphabetCount[alpha] == 0)    count++;      // 0이면 첫 등장 이므로, 알파벳 종류 개수 + 1
        alphabetCount[alpha]++;
      }
    }

    perm(0, new boolean[26], new int[count]);

    System.out.println(max);
    br.close();
  }

  static void perm(int cnt, boolean[] visited, int[] res) {
    if (cnt == res.length) {            // 순열 완성
      int[] charToNum = new int[26];    // 알파벳에 숫자를 부여한 것을 저장할 배열

      int idx = 0;
      for (int i=0; i<26; i++)
        if (alphabetCount[i] != 0)     // 등장했던 알파벳에 대해서
          charToNum[i] = res[idx++];   // 순열 순서대로 숫자를 부여

      // 등장한 알파벳에 숫자를 부여한 후에
      // 주어진 문자열 words를 정수로 변환하여 result에 누적한다.
      long result = 0L;
      for (String word : words) {
        long wtl = wordToLong(word, charToNum);
        result += wtl;
      }

      if (result > max)   // max값 업데이트
        max = result;

      return;
    }

    // Permutation
    for (int i=0; i<10; i++) {
      if (visited[i])
        continue;
      visited[i] = true;
      res[cnt] = i;
      perm(cnt+1, visited, res);
      visited[i] = false;
    }
  }

  // 문자열 word를 charToNum 배열을 통해서 각 글자를 정수로 변환
  static long wordToLong(String word, int[] charToNum) {
    StringBuilder resultSb = new StringBuilder();

    for (char c : word.toCharArray())
      resultSb.append(charToNum[c-'A']);

    return Long.parseLong(resultSb.toString());
  }
}

OFF THE RECORD

문제는 해결했지만 시간을 단축시키는 다른 방법을 좀 더 고민하던 중, 탐욕법으로 해결할 만한 실마리를 찾았다. 길이가 긴 단어의 앞글자에게 큰 숫자를 부여하자라는 방법이었는데, 테스트 케이스는 다 통과했지만 반례가 있었다.

1
2
3
4
3
ABCDE
CBAAZ
ZZZZZ

위와 같이 입력이 주어지면 ZZZZZ를 큰 수로 만들기 위해서 ‘Z’가 9를 가져야하는 것을 직관적으로 알 수 있다.

하지만, 생각했던 방식에 따르면 길이가 같지만 먼저 등장한 ABCDE의 첫 글자 ‘A’가 9를 가지게 되므로 예외가 생긴다. 따라서, 각 알파벳이 등장한 자리수를 고려해야하는 것을 알았지만, 해결하는 방법은 찾지 못해 다른 사람의 풀이를 참조했다.

수학적으로 접근하여 각 알파벳이 등장한 자리수를 표현할 수 있었다. 위 예시를 그대로 사용하자면 ABCDE는 아래와 같은 것이다.

A $ 10^4 = 10000 $
B $ 10^3 = 1000 $
C $ 10^2 = 100 $
D $ 10^1 = 10 $
E $ 10^0 = 1$

CBAAZ까지 적용하면 아래와 같다.

A $ 10^4 + 10^2 + 10^1 = 10110 $
B $ 10^3 + 10^3 = 2000 $
C $ 10^4 + 10^2 = 10100 $
D $ 10^1 = 10 $
E $ 10^0 = 1 $
Z $ 10^0 = 1 $

ZZZZZ까지 적용하면 아래와 같다.

A $ 10^4 + 10^2 + 10^1 = 10110 $
B $ 10^3 + 10^3 = 2000 $
C $ 10^4 + 10^2 = 10100 $
D $ 10^1 = 10 $
E $ 10^0 = 1 $
Z $ 10^4 + 10^3 + 10^2 + 10^1 + 10^0 + 10^0 = 11112 $

즉 ‘Z’가 높은 숫자를 가져야 하는 것을 위와 같이 수치로 확인할 수 있고, 위 값에 따라 정렬한 후에 숫자를 부여하면 최대값을 구할 수 있다.

댓글 남기기