[Baekjoon / JAVA] Baekjoon Algorithm Problem 1019 - Book Pages
[Baekjoon / JAVA] Baekjoon Algorithm Problem 1019 - Book Pages
| Rank | Language Used |
|---|---|
🖼️ JAVA |
| Time Limit | Memory Limit |
|---|---|
| 2 sec | 128MB |
Jimin has a book with a total of pages. The first page is page 1, and the last page is page . Let's find out how many times each digit appears across all the page numbers.
The first line gives . is a natural number less than or equal to .
On the first line, print how many times 0 appears in total, how many times 1 appears, ..., and how many times 9 appears, separated by spaces.
- Input
TC
11
- Output
TC
1 4 1 1 1 1 1 1 1 1
The problem is clear and intuitive. When listing pages from page 1 to page , this problem asks us to count how many times each digit was used.
To write the number 165, the digits are used. In this way, we need to count how many times each digit from 0 was used to write out every number from 1 to the given number, and print them in ascending order starting from 0.
In other words, if , the pages listed are . The table below shows how many times each digit was used.
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
|---|---|---|---|---|---|---|---|---|---|
| 0 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 0 | 0 |
Listing from 1 to 5, each digit is used exactly once, so it can be shown as above. So what about the example value 11?
The numbers listed are .
From 1 to 9, each digit is used once each; 10 uses 1 and 0; and 11 uses 1 twice.
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
|---|---|---|---|---|---|---|---|---|---|
| 1 | 4 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
Each digit was used as many times as shown in the table above. To help with understanding, let's also try .
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
|---|---|---|---|---|---|---|---|---|---|
| 1 | 4 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
, . Since 1 to 13 also includes 11, we can just add the values for 12 and 13 to the result for 11.
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
|---|---|---|---|---|---|---|---|---|---|
| 1 | 6 | 2 | 2 | 1 | 1 | 1 | 1 | 1 | 1 |
I think this is enough for you to understand what the algorithm needs to do.
Honestly, if you approach this brute-force, it isn't that hard a problem. Just loop through each number, break it into its digits, and add each one to the corresponding count. Unfortunately, though, the maximum value of is close to one billion (luckily, it doesn't exceed the max value of int). That means a brute-force approach won't cut it.
So we need to find some pattern hidden somewhere and design a general formula. In situations like this, listing things out one by one usually reveals it.
| N | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
|---|---|---|---|---|---|---|---|---|---|---|
| 1 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 2 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 3 | 0 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 |
| 4 | 0 | 1 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 0 |
| 5 | 0 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 0 | 0 |
| 6 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 0 |
| 7 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 |
| 8 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 |
| 9 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
| 10 | 1 | 2 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
| 11 | 1 | 4 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
| 12 | 1 | 5 | 2 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
| 13 | 1 | 6 | 2 | 2 | 1 | 1 | 1 | 1 | 1 | 1 |
| 14 | 1 | 7 | 2 | 2 | 2 | 1 | 1 | 1 | 1 | 1 |
| 15 | 1 | 8 | 2 | 2 | 2 | 2 | 1 | 1 | 1 | 1 |
| 16 | 1 | 9 | 2 | 2 | 2 | 2 | 2 | 1 | 1 | 1 |
| 17 | 1 | 10 | 2 | 2 | 2 | 2 | 2 | 2 | 1 | 1 |
| 18 | 1 | 11 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 1 |
| 19 | 1 | 12 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 |
| 20 | 2 | 12 | 3 | 2 | 2 | 2 | 2 | 2 | 2 | 2 |
| 21 | 2 | 13 | 4 | 2 | 2 | 2 | 2 | 2 | 2 | 2 |
| 22 | 2 | 13 | 6 | 2 | 2 | 2 | 2 | 2 | 2 | 2 |
| 23 | 2 | 13 | 7 | 3 | 2 | 2 | 2 | 2 | 2 | 2 |
I went a little past 20 to lay out the digit usage counts and look for patterns. Some kind of pattern does seem to appear.
- 0 increases by 1 with every multiple of 10.
- Each digit in the ones place increases by 1 for the corresponding number, and its final value is equal to the tens digit + 1.
- The tens digit increases the corresponding number by 1.
Just staring at it, it can be a bit hard to spot the pattern. The answer lies in the range *0 through *9. For example, let's list out 10 through 29. Rather than starting from 1, let's assume we're computing the algorithm based on an arbitrary range through .
| Digit Layout | |||||||||
|---|---|---|---|---|---|---|---|---|---|
| 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
| 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 |
Looking closely at the numbers in the table above, you can see that each ones digit is used exactly once per row.
| Digit Layout | |||||||||
|---|---|---|---|---|---|---|---|---|---|
| 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
| 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 |
Now the pattern starts to stand out a bit. In a range of the form *0 through *9, such as 20 through 39, every digit in the ones place is used the same number of times. Since there are two such ranges here — 10 to 19 and 20 to 29 — every digit is used twice, once per range.
If we call the starting page and the ending page , this rule can be written as a general formula as follows.
Therefore, we can see that in the range 10 to 29, every digit is used exactly twice.
The problem is that the formula above only applies to the ones place. Pages can have up to 10 digits. In other words, we need a general formula that applies universally.
| Digit Layout | |||||||||
|---|---|---|---|---|---|---|---|---|---|
| 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
| 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 |
Conversely, let's take a close look at the tens digit. 1 is used 10 times. If the range were 100 to 199, 1 would be used 100 times, and if the range were 1000 to 1999, 1 would be used 1000 times.
For convenience, let's define a "unit range" as a range of the form , like 10 to 19, 100 to 199, or 1000 to 1999. In that case, the count of times is used in that range can be defined as follows.
- : starting value of the range
- : ending value of the range
- : place value
Now, as long as we have a matching range, we can find the count of a given digit — but this is still limited.
First, in this algorithm, the starting value is always fixed at 1. The ending value also doesn't necessarily come in as a unit range like 199. If , we need to apply the algorithm to the range 1 to 35. Unless the range happens to be something like 10 to 39, the general formula above doesn't directly apply to a range with a completely different shape.
The solution is simple. Just like adjusting for windage by adding and subtracting corrections, we add and subtract values to bring the range into the right shape.
In the range 1 to 35, for the number 1: the nearest value greater than 1 that includes a 0 is 10. So we increase the starting value up to 10, counting each number as we go. Since we count from 1 through 9, this can be shown in a table as follows.
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
|---|---|---|---|---|---|---|---|---|---|
| 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
For 35, the nearest value less than 35 that includes a 9 is 29. Similarly, we decrease the ending value down to 29, counting each number as we go. Since we count from 35 down to 30, this can be shown in a table as follows.
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
|---|---|---|---|---|---|---|---|---|---|
| 1 | 1 | 1 | 7 | 1 | 1 | 0 | 0 | 0 | 0 |
In other words, the initial value is the array with these correction values already added, and subsequent calculations are accumulated on top of this initial value.
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
|---|---|---|---|---|---|---|---|---|---|
| 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
| 1 | 1 | 1 | 7 | 1 | 1 | 0 | 0 | 0 | 0 |
| 1 | 2 | 2 | 8 | 2 | 2 | 1 | 1 | 1 | 1 |
When and , the count of digits used in the ones place is computed as follows.
Each digit is used 100 times in the ones place. What about the tens place?
Dividing and each by 10 gives us the range for the tens place. We apply the divided values to the general formula above.
For the hundreds place, we compute by dividing and each by 100.
For the thousands place, we compute by dividing and each by 1000. But since , only the digit 1 gets 1000 uses.
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | |
|---|---|---|---|---|---|---|---|---|---|---|
| 1 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 |
| 10 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 |
| 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 |
| 1000 | 0 | 1000 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 300 | 1300 | 300 | 300 | 300 | 300 | 300 | 300 | 300 | 300 |
So the range 1000 to 1999 is computed as shown above.
For a full understanding, let's use these concepts to compute the algorithm for .
Since , the range is 1 to 4153.
We move up to 10, the nearest number greater than 1 whose ones digit is 0, counting each number we pass along the way separately.
We move from 1 through 9 to reach 10, so we count 1 through 9 separately.
| Group | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
|---|---|---|---|---|---|---|---|---|---|---|
| 1 ~ 9 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
We move down to 4149, the nearest number less than 4153 whose ones digit is 9, counting each number we pass along the way separately.
We move from 4153 down to 4149, so we count these separately.
| Group | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
|---|---|---|---|---|---|---|---|---|---|---|
| 4153 | 0 | 1 | 0 | 1 | 1 | 1 | 0 | 0 | 0 | 0 |
| 4152 | 0 | 1 | 1 | 0 | 1 | 1 | 0 | 0 | 0 | 0 |
| 4151 | 0 | 2 | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 0 |
| 4150 | 1 | 1 | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 0 |
Now that we have a range 10 to 4149 that the general formula can be applied to, let's apply it.
| Group | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
|---|---|---|---|---|---|---|---|---|---|---|
| Ones place | 414 | 414 | 414 | 414 | 414 | 414 | 414 | 414 | 414 | 414 |
To compute the next digit place, we divide the ones-place general formula's range values, 4149 and 10, each by 10.
The range for the tens place becomes . Similarly, we adjust the range for the general formula to apply. Since this is the tens place, note that a move from 1 to 2 actually corresponds to a move from 10 to 20.
| Group | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
|---|---|---|---|---|---|---|---|---|---|---|
| 1 ~ 9 | 0 | 10 | 10 | 10 | 10 | 10 | 10 | 10 | 10 | 10 |
| Group | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
|---|---|---|---|---|---|---|---|---|---|---|
| 414 | 0 | 10 | 0 | 0 | 20 | 0 | 0 | 0 | 0 | 0 |
| 413 | 0 | 10 | 0 | 10 | 10 | 0 | 0 | 0 | 0 | 0 |
| 412 | 0 | 10 | 10 | 0 | 10 | 0 | 0 | 0 | 0 | 0 |
| 411 | 0 | 20 | 0 | 0 | 10 | 0 | 0 | 0 | 0 | 0 |
| 410 | 10 | 10 | 0 | 0 | 10 | 0 | 0 | 0 | 0 | 0 |
Now that we have a range 10 to 409 that the general formula can be applied to, let's apply it.
| Group | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
|---|---|---|---|---|---|---|---|---|---|---|
| Tens place | 400 | 400 | 400 | 400 | 400 | 400 | 400 | 400 | 400 | 400 |
To compute the next digit place, we divide the tens-place general formula's range values, 409 and 10, each by 10.
The range for the hundreds place becomes . The rest of the process is the same as for the tens place.
| Group | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
|---|---|---|---|---|---|---|---|---|---|---|
| 1 ~ 9 | 0 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 |
| Group | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
|---|---|---|---|---|---|---|---|---|---|---|
| 40 | 100 | 0 | 0 | 0 | 100 | 0 | 0 | 0 | 0 | 0 |
Now that we have a range 10 to 39 that the general formula can be applied to, let's apply it.
| Group | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
|---|---|---|---|---|---|---|---|---|---|---|
| Hundreds place | 300 | 300 | 300 | 300 | 300 | 300 | 300 | 300 | 300 | 300 |
To compute the next digit place, we divide the hundreds-place general formula's range values, 39 and 10, each by 10.
The range for the thousands place becomes .
There's one issue here: for the final range, the smallest value whose ones digit is 9 would be -9. Since negative numbers can't occur, no further general-formula computation is possible, so we just add these values individually.
| Group | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
|---|---|---|---|---|---|---|---|---|---|---|
| 1 ~ 3 | 0 | 1000 | 1000 | 1000 | 0 | 0 | 0 | 0 | 0 | 0 |
Let's organize everything computed at each stage into a table of grand totals.
| Group | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
|---|---|---|---|---|---|---|---|---|---|---|
| 1 ~ 9 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
| 4153 | 0 | 1 | 0 | 1 | 1 | 1 | 0 | 0 | 0 | 0 |
| 4152 | 0 | 1 | 1 | 0 | 1 | 1 | 0 | 0 | 0 | 0 |
| 4151 | 0 | 2 | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 0 |
| 4150 | 1 | 1 | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 0 |
| Ones place | 414 | 414 | 414 | 414 | 414 | 414 | 414 | 414 | 414 | 414 |
| 1 ~ 9 | 0 | 10 | 10 | 10 | 10 | 10 | 10 | 10 | 10 | 10 |
| 414 | 0 | 10 | 0 | 0 | 20 | 0 | 0 | 0 | 0 | 0 |
| 413 | 0 | 10 | 0 | 10 | 10 | 0 | 0 | 0 | 0 | 0 |
| 412 | 0 | 10 | 10 | 0 | 10 | 0 | 0 | 0 | 0 | 0 |
| 411 | 0 | 20 | 0 | 0 | 10 | 0 | 0 | 0 | 0 | 0 |
| 410 | 10 | 10 | 0 | 0 | 10 | 0 | 0 | 0 | 0 | 0 |
| Tens place | 400 | 400 | 400 | 400 | 400 | 400 | 400 | 400 | 400 | 400 |
| 1 ~ 9 | 0 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 |
| 40 | 100 | 0 | 0 | 0 | 100 | 0 | 0 | 0 | 0 | 0 |
| Hundreds place | 300 | 300 | 300 | 300 | 300 | 300 | 300 | 300 | 300 | 300 |
| 1 ~ 3 | 0 | 1000 | 1000 | 1000 | 0 | 0 | 0 | 0 | 0 | 0 |
| Total | 1225 | 2290 | 2236 | 2236 | 1389 | 1229 | 1225 | 1225 | 1225 | 1225 |
The algorithm's result for the range 1 to 4153 is as shown above.
JAVA
import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; /** * 백준 전체 1019 문제 알고리즘 클래스 * * @author RWB * @see <a href="https://blog.itcode.dev/posts/2021/06/28/a1019">1019 풀이</a> * @since 2021.06.28 Mon 12:28:50 */ public class Main { // 숫자 카운트 배열 private static final int[] COUNTER = new int[10]; /** * 메인 함수 * * @param args: [String[]] 매개변수 * * @throws IOException 데이터 입출력 예외 */ public static void main(String[] args) throws IOException { BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(System.out)); // 마지막 페이지 int N = Integer.parseInt(reader.readLine()); solve(N); StringBuilder builder = new StringBuilder(); for (int item : COUNTER) { builder.append(item).append(" "); } writer.write(builder.toString().trim()); writer.newLine(); writer.flush(); reader.close(); writer.close(); } /** * 알고리즘 동작 함수 * * @param num: [int] 마지막 페이지 */ private static void solve(int num) { // 시작 페이지 int start = 1; // 자릿수 int digit = 1; while (start <= num) { // 1의 자리가 9가 될 때까지 마지막 페이지를 1씩 감소함 while (num % 10 != 9 && start <= num) { // 감소한 페이지 별도 카운팅 count(num, digit); num--; } // 마지막 페이지가 시작 페이지보다 작을 경우 if (num < start) { // 이를 처리하지 않으면 num < 9일 경우 무한루프를 탐 break; } // 1의 자리가 0이 될 때까지 시작 페이지를 1씩 증가함 while (start % 10 != 0 && start <= num) { // 증가한 페이지 별도 카운팅 count(start, digit); start++; } start /= 10; num /= 10; for (int i = 0; i < 10; i++) { COUNTER[i] += (num - start + 1) * digit; } // 자릿수 증가 digit *= 10; } } /** * 카운트 함수 * * @param num: [int] 대상 숫자 * @param digit: [int] 자릿수 */ private static void count(int num, int digit) { while (num > 0) { COUNTER[num % 10] += digit; num /= 10; } } }
When , during the process of adjusting the range, numbers like 4153 and 4152 need to be counted individually.
JAVA
/** * 카운트 함수 * * @param num: [int] 대상 숫자 * @param digit: [int] 자릿수 */ private static void count(int num, int digit) { while (num > 0) { counter[num % 10] += digit; num /= 10; } }
The logic isn't difficult. Since 4152 consists of the digits , all you need to do is add the digit place value (1 for ones, 10 for tens, and so on) to the count for each corresponding digit.
The ones digit can be found with . The tens digit can be found by dividing 4152 by 10 once and repeating the same operation. The hundreds place, thousands place, and so on can be computed by repeating this as many times as there are digits.
JAVA
/** * 알고리즘 동작 함수 * * @param num: [int] 마지막 페이지 */ private static void solve(int num) { // 시작 페이지 int start = 1; // 자릿수 int digit = 1; while (start <= num) { // 1의 자리가 9가 될 때까지 마지막 페이지를 1씩 감소함 while (num % 10 != 9 && start <= num) { // 감소한 페이지 별도 카운팅 count(num, digit); num--; } // 마지막 페이지가 시작 페이지보다 작을 경우 if (num < start) { // 이를 처리하지 않으면 num < 9일 경우 무한루프를 탐 break; } // 1의 자리가 0이 될 때까지 시작 페이지를 1씩 증가함 while (start % 10 != 0 && start <= num) { // 증가한 페이지 별도 카운팅 count(start, digit); start++; } start /= 10; num /= 10; for (int i = 0; i < 10; i++) { counter[i] += (num - start + 1) * digit; } // 자릿수 증가 digit *= 10; } }
The starting page is always fixed at 1. We repeat until the starting page exceeds the ending page.
In the first while loop, we decrease the ending page by 1 at a time, adjusting it into a range ending in 9. There's a condition in the middle of it — without this handling, if num is smaller than 9, start would never exceed num during the process, causing an infinite loop.
The second while loop increases page 1 by 1 at a time, adjusting it into a range starting at 0. All adjusted values are counted separately through the count method.
Once the range has been adjusted through this process, all that's left is to apply the formula mentioned above and repeat it.
- Mathematics

