본문 바로가기

[Project Euler] Coding Quiz 2

https://euler.synap.co.kr/quiz=2

 

 

 

 

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
import java.io.BufferedWriter;
import java.io.FileReader;
import java.util.List;
import java.util.ArrayList;
 
public class Q002 {
    public static void main(String args[]) {
      System.out.println(run());
    }
    
    public static String run() {
        int sum = 0;
        int countSyllables, countWords;
        String[] subUnits = new String[]{"""십""백""천"};
        String[] superUnits = new String[]{"""만""억""조"};
        String[] values = new String[]{"""일""이""삼""사""오""육""칠""팔""구"};
        
        try {
            BufferedReader br = new BufferedWriter(new FileWriter(/*Your file*/));
            
            String str = null;
            while((str = br.readLine()) != null) {
                str = str.replaceAll("[^\\d]+""");
                int len = str.length();
                StringBuilder result = new StringBuilder();
                List<String> tokens = new ArrayList<String>(0);
                
                if(len%4 != 0) { tokens.add(str.substring(0, len%4)); }
                for(int i = 0;
                    i < len >> 2;
                    tokens.add(str.substring(len%4 + (i++ << 2), len%4 + (i << 2))));
                
                for(int i = 0; i < tokens.size(); i++) {
                    String token = tokens.get(i);
                    if(token.compareTo("0000"> 0) {
                        result.append(" ");
                        for(int k = 0; k < token.length() - 1; k++) {
                            char c = token.charAt(k);
                            if(c > '1') { result.append(values[c - '0']); }
                            if(c > '0') { result.append(subUnits[token.length() - 1 - k]); }
                        }
                        
                        result.append(values[token.charAt(token.length() - 1- '0']);
                        result.append(superUnits[tokens.size() - 1 - i]);
                    }
                }
                
                String s = result.toString().replace(" 일만"" 만").trim() + "원";
                countSyllables = s.length() - ((countWords = s.split(" ").length- 1);
                sum += (countSyllables*countWords);
            }
        } catch(Exception e) {
            return "-1";
        }
        
        return Integer.toString(sum);
    }
}
cs

 

12 ~ 16행:

필요한 변수들을 선언했다.

 sum : 한글로 변환된 단어의 어절 수와 총 길이의 곱의 합이 저장된다.

 countSyllable countWords : 각 단어의 음절 수와 어절 수가 저장된다.

 subUnits : '오천이백삼십사억' 등의 금액에서 천, 백, 십은 억, 만과 같은 '상위 단위'의 하위 단위, 즉 sub unit으로 본다.

 superUnits : 마찬가지로 '오천이백삼십사억' 등의 금액에서 만, 억, 조 등은 '상위 단위'에 해당한다. 즉 super unit에 해당한다고 본다.

 values : 십, 백, 천, 만, 억, 조 등은 단위에 해당하고 일, 이, 삼, ..., 팔, 구는 값에 해당한다.

모든 금액은 값과 단위의 조합으로 구성된다.

 

 

19행:

파일에서 읽어 온다. 나는 다음과 같은 텍스트 파일(.txt)에서 읽어 왔다.

 

한 줄씩 읽는  readLine()  메소드를 사용하면서 빠르게 읽기 위해  BufferedReader 로 읽어 온다.

 

21 ~ 22행:

 readLine()  메소드는 파일에서 더이상 읽을 내용이 없을 경우  null 을 반환한다. 따라서 반복 조건은 ' readLine()  메소드가  null 을 반환할 때까지'가 된다.

 

23행:

읽어온 문자열에서 숫자만 남기고 모두 지운다. 여기서는 comma(,)와 '원'을 지우는 작업에 해당한다.

 

24행:

숫자만 남은 문자열의 길이를 얻는다.

 

25 ~ 31행:

읽어온 문자열은 숫자만 남긴 후 토큰화(tokenize)해서 토큰 리스트에 저장한다.

문자열의 길이( len )가 4의 배수가 아닌 경우 먼저 4로 나눈 나머지 인덱스까지를 잘라낸다.

예를 들어 98741035486이라면 길이는 11이고 한글 읽기는 '987억 4103만 5486원'이 된다. 

따라서  11%4 = 3 번째 문자까지 잘라낸 후, 나머지를 4글자씩 잘라 토큰화한다.

그리고 토큰들을 토큰 리스트( tokens )에 저장한다. 

토큰들의 순서는 유지되어야 하므로  Set 이 아닌  List 를 사용한다.

 

33 ~ 35행:

 tokens 에서 토큰들을 하나씩 꺼내온다.

만약 이 토큰이 "0000"이라면 금액을 표시할 필요가 없으므로 "0000"보다 큰 문자열,

즉 "0001" ~ "9999"일 때만 한글 읽기로 변환한다.

그리고 37 ~ 44행을 거치면 각 토큰에 해당하는 한글 읽기로 변환된다.

 

36행:

변환된 한글 읽기의 맨 앞에 공백이 오게 하기 위해 변환 작업 전에  StringBuilder 에 공백 하나를 추가한다.

 

37행:

토큰에서 한 글자씩(= 숫자 한 개씩) 읽어온다. 맨 마지막 글자는 제외한다.

읽어온 숫자가 2 ~ 9면  StringBuilder 에 "이" ~ "구"를 추가한다.

일천, 일백, 일십이라는 값은 없으므로 "일"은 붙이지 않는다.

인덱스는  '1' - '0' = 1 '2' - '0' = 2 이므로 각 값에 해당하는 문자가 붙게 된다.

 

그리고 1 ~ 9면 글자의 인덱스에 따라 천, 백, 십의 하위 단위를 추가한다.

즉 맨 처음 값에는 천, 그 다음은 백, 그 다음은 십이 붙게 된다.

토큰의 길이가  4 라면  k 는  0 ~ 2 의 값을 갖게 되므로

 subUnits 의 인덱스는  4 - 1 - 0 ~ 4 - 1 - 2  3 ~ 1 가 된다.

따라서 천, 백, 십이 차례대로 추가된다.

""는 인덱스를 맞추기 위해 넣은 요소고 한글 읽기로 변환하는 데에 쓰이지는 않는다.

 

결론적으로 읽어온 토큰이 "1024"라면 "천이십사"가 된다.

 

이제 토큰의 마지막 글자에 대한 처리가 이루어진다.

마지막 문자에 해당하는 값(이 ~ 구)를 추가한 후 상위 단위를 추가한다.

전체 토큰 수가  4 라면  i 는  0 ~ 3 의 값을 갖게 되므로 

 superUnits 의 인덱스는  4 - 1 - 0 ~ 4 - 1 - 3  3 ~ 0 이 된다.

따라서 "", 만, 억, 조가 차례대로 추가된다.

""는 맨 마지막 토큰, 즉 상위 단위가 없는 부분에서 추가된다.

 

48~ 50행:

이렇게 변환된 문자열에서 " 일만"을 " 만"으로 바꾼 후 앞뒤 공백을 제거( trim() )한다.

그리고 "원"을 붙인 다음 공백을 제외한 음절 수와 어절 수를 구한다.

그리고 그들의 곱을  sum 에 더한다.

 

이 때 " 일만"을 " 만"으로 바꾸는, 이유는 다음과 같다.

우선 만이라는 단위는 상위 단위에 해당하지만 일조, 일억과 다르게 일만은 없다.

즉 '예외'에 해당하므로 이 경우에만 따로 변환이 한번 더 이루어진다.

이런 작업이 모든 금액에 대해 일반화될 수 있는 이유는 36행에서 무조건 공백을 먼저 추가한 후 토큰을 변환하기 때문이다.

가령 주어진 금액이 10,000원이라면, 이를 10000으로 바꾸고 "1""0000"의 토큰으로 나눈다.

만약 " 일만"을 " 만"으로 바꾸지 않는다면 (공백을 먼저 더한 후 변환이 이루어지므로) " 일만원"이 된다. 

 

문제에서 주어진 6,278,000,015,000원의 경우도 마찬가지다.

"6""2780""0001""5000"의 토큰으로 나누어 "0001"은 " 일만"으로 바뀌고,

결론적으로 " 육조 이천칠백팔십억 일만 오천원"이 된다.

 

따라서 먼저 " 일만"" 만"으로 바꾼 후 나머지 처리를 해야 하는 것이다.

'Programming > Solutions' 카테고리의 다른 글

[Project Euler] Problem 022  (0) 2021.02.13
[Project Euler] Problem 026  (0) 2021.01.21
[Project Euler] Problem 005  (0) 2021.01.16
[Project Euler] Coding Quiz 4  (0) 2021.01.15
[Project Euler] Coding Quiz 1  (0) 2021.01.12