<문제>
여기 5천개 이상의 영문 이름들이 들어있는 46KB짜리 텍스트 파일 names.txt 이 있습니다 (우클릭해서 다운로드 받으세요).
이제 각 이름에 대해서 아래와 같은 방법으로 점수를 매기고자 합니다.
- 먼저 모든 이름을 알파벳 순으로 정렬합니다.
- 각 이름에 대해서, 그 이름을 이루는 알파벳에 해당하는 수(A=1, B=2, ..., Z=26)를 모두 더합니다.
- 여기에 이 이름의 순번을 곱합니다.
예를 들어 "COLIN"의 경우, 알파벳에 해당하는 수는 3, 15, 12, 9, 14이므로 합이 53, 그리고 정렬했을 때 938번째에 오므로 최종 점수는 938 × 53 = 49714가 됩니다.
names.txt에 들어있는 모든 이름의 점수를 계산해서 더하면 얼마입니까?
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
|
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class Problem022 {
public static void main(String[] args) {
System.out.print(run());
}
public static String run() {
File file = new File("names.txt");
int seq = 0, result = 0;
List<String> list = new ArrayList<String>(0);
try {
BufferedReader br = new BufferedReader(new FileReader(file));
String s = null;
while((s = br.readLine()) != null) {
list.add(s.substring(s.indexOf('\"') + 1, s.lastIndexOf('\"')));
} br.close();
String[] arr = new String[list.size()];
for(int i = 0; i < list.size(); arr[i] = list.get(i++));
Arrays.sort(arr);
for(int i = 0; i < arr.length; i++) {
int sum = 0;
for(int j = 0; j < arr[i].length(); j++) {
sum += (arr[i].charAt(j) - 'A' + 1);
}
result += (++seq*sum);
}
} catch (Exception e) {
e.printStackTrace();
}
return Integer.toString(result);
}
}
|
cs |
names.txt에는 5163개의 이름이 단 하나의 개행도 없이 한 줄에 써져 있다. 세상에..
사실 이 부분이 어렵다. 개행이 없기 때문에 한 줄로 읽을 수도 없고, 문자열이 너무 길어서 읽어온 다음 토큰화하기도 여의치 않다(파이썬은 될 지도 모르겠다).
notepad++로 names.txt에서 콤마(,)를 개행(\r\n)으로 바꾼 후에 풀었다.
15행 ~ 16행:
5163개의 이름에 매겨질 순번 변수, 결과값이 저장될 변수, 이름 리스트 선언.
18행:
원래는 FileReader(file) 대신 InputStreamReader(new FileInputStream(file), ENCODING)으로 인코딩을 지정해주는 편인데, 지금은 그런 부분이 중요한 게 아니라 그냥 전자의 방법으로 했다.
22행:
현재 파일에는 이름들이
"이름1"
"이름2"
...
"이름n"
의 형태로 저장되어 있기 때문에 큰 따옴표를 잘라낸 문자열을 리스트에 저장한다.
25 ~ 27행:
이 문제는 크게 세 부분으로 나눌 수 있다.
① 이름 데이터 정렬
② 이름 별로 알파벳에 대응하는 숫자 값 합산
③ ②에서 합산한 값에 이름의 순번 값을 곱한 후 합산
여기서는 ①을 수행한다.
리스트의 문자열들을 배열로 옮긴 후 Arrays.sort() 로 정렬한다. List 인터페이스에도 정렬 메소드가 있지만 그러러면 비교자까지 선언해줘야 돼서 그냥 배열로 옮겼다. 그리고 어차피 List의 정렬 메소드도 내부적으로는 배열로 옮겨서 정렬하기 때문에 굳이 쓸 필요성을 못 느꼈다.
그리고 배열로 옮기는 것보다는 정렬이 목적이기 때문에 깊은 복제(객체까지 복제)가 아닌 얕은 복제(참조만 복제)의 형태로 옮겼다.
29 ~ 33행:
여기서는 ②을 수행한다.
배열에서 문자열을 하나 읽어온 후 문자열에서 각 알파벳에 대응하는 값 sum에 합산한다.
'A' = 1, 'B' = 2, 'C' = 3, ..., 'Z' = 26에 대응시키려면 알파벳에서 'A'를 뺀 후 1을 더하면 된다.
35행:
여기서는 ③을 수행한다.
앞에서 합산한 값(sum)에 순번(seq)를 곱해 result에 더한다. seq는 0으로 초기화한 후 여기서 선위 증가 연산자로 1부터 차례대로 순번이 매겨지게 했다.
42행:
결과 값을 문자열 형태로 반환한다.
처음에는 notepad++에서 파일 내용을 수정한 후 풀었는데, 풀고 보니 안 그래도 되겠다 싶어서 다시 풀었다.
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
|
import java.io.File;
import java.io.FileReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class Problem022 {
public static void main(String[] args) {
System.out.print(run());
}
public static String run() {
File file = new File("names.txt");
int seq = 0, result = 0;
List<String> list = new ArrayList<String>(0);
try {
FileReader fr = new FileReader(file);
int b = -1;
StringBuilder sb = new StringBuilder();
while((b = fr.read()) != -1) {
if(('A' <= b) && (b <= 'Z')) {
sb.append((char) b);
} else if(b == ((int)',')) {
list.add(sb.toString());
sb = new StringBuilder();
}
} fr.close(); list.add(sb.toString()); //The last string doesn't contain a comma
String[] arr = new String[list.size()];
for(int i = 0; i < list.size(); arr[i] = list.get(i++));
Arrays.sort(arr);
for(int i = 0; i < arr.length; i++) {
int sum = 0;
for(int j = 0; j < arr[i].length(); j++) {
sum += (arr[i].charAt(j) - 'A' + 1);
}
result += (++seq*sum);
}
} catch (Exception e) {
e.printStackTrace();
}
return Integer.toString(result);
}
}
|
cs |
달라진 것은 파일에서 읽어오는 부분 뿐이고, 30행부터는 결과를 구하는 부분이므로 앞의 코드와 동일하다.
19행:
~Writer 클래스의 read() 메소드는 파일에서 문자 하나를 읽어온 후 문자의 유니코드 값을 int 형으로 반환한다.
따라서 문자 코드 값을 저장할 변수는 int 형으로 선언한다.
20행:
문자를 하나씩 추가해 문자열을 형성하기 위해 StringBuilder 변수를 선언한다.
22 ~ 27행:
각 이름은 큰 따옴표로 둘러싸여 있고, 콤마로 구분되어 있다.
따라서 문자를 하나씩 읽으면서 알파벳일 경우 StringBuilder에 추가하고, 콤마일 경우 이름 하나가 끝났음을 의미하므로 현재 StringBuilder로 만들어진 문자열을 리스트에 추가하고 새로운 StringBuilder를 만든다.
큰 따옴표는 큰 따옴표에 대한 조건문을 생략함으로써 무시한다.
28행:
마지막 이름은 뒤에 콤마가 없다. StringBuilder에는 마지막 이름이 저장되어 있지만 리스트에는 추가되지 않은 상태로 반복문을 탈출하게 되는 것이다. 따라서 마지막 이름을 list에 추가해준다.
그 다음부터는 위와 동일하므로 생략.
'Programming > Solutions' 카테고리의 다른 글
[BAEKJOON] 1010번 (0) | 2021.03.05 |
---|---|
[BAEKJOON] 1004번 (0) | 2021.03.05 |
[Project Euler] Problem 026 (0) | 2021.01.21 |
[Project Euler] Coding Quiz 2 (0) | 2021.01.20 |
[Project Euler] Problem 005 (0) | 2021.01.16 |