Digit DP
This page provides a brief introduction to Digit DP.
Introduction
Digit refers to splitting a number into its individual digits according to place values such as ones, tens, hundreds, thousands, etc. If splitting a decimal number, each digit ranges from 0 to 9; other bases can be analogized to decimal.
Digit DP: Used to solve a specific class of problems, which are generally recognizable by the following characteristics:
- It requires counting the number of integers satisfying certain conditions (i.e., the ultimate goal is counting);
- These conditions can be understood and judged using the idea of "digits" after transformation;
- The input provides a numeric interval (sometimes only an upper bound) as a constraint for counting;
- The upper bound is large (e.g., \(10^{18}\)), making brute-force enumeration and verification time out.
The basic principle of Digit DP:
Consider how humans count; the most naive counting method is incrementing one by one from small to large. However, we notice that for numbers with many digits, this process has many repetitive parts. For example, counting from 7000 to 7999, from 8000 to 8999, and from 9000 to 9999 are very similar processes: the last three digits change from 000 to 999, with only the thousands digit differing. Therefore, we can merge these processes and store the counting answers generated during these processes in a general array. This array sets states according to the specific requirements of the problem and performs state transitions using recursion or DP.
Digit DP often utilizes general counting problem techniques, such as splitting the answer within an interval into two parts by subtraction (i.e., \(\mathit{ans}_{[l, r]} = \mathit{ans}_{[0, r]}-\mathit{ans}_{[0, l - 1]}\)).
With the general answer array, the next step is to compute the answer. The answer can be computed using memoized search or iterative DP. To count all answers not exceeding the upper bound without repetition or omission, we enumerate each digit from high to low, consider which digits can be filled in each position, and finally use the general answer array to compute the answer.
Next, let's look at a few specific problems.
Example 1
Example 1 Luogu P2602 Digit Count
Problem summary: Given two positive integers \(a,b\), find how many times each digit appears in all integers in \([a,b]\).
Method 1
Explanation
Observe that for numbers with exactly \(\mathit{i}\) digits, the occurrence counts of all digits are the same. Let array \(\mathit{dp}_i\) represent the occurrence count of each digit in numbers with exactly \(i\) digits, ignoring leading zeros for now. Then \(\mathit{dp}_i=10 \times \mathit{dp}_{i−1}+10^{i−1}\), where the first part comes from the contribution of the first \(i-1\) digits, and the second part comes from the contribution of the \(i\)-th digit.
With the \(\mathit{dp}\) array, we consider how to compute the answer. Split the upper bound into digits, enumerate from high to low. When not tight to the upper bound, the remaining digits can be chosen arbitrarily. When tight to the upper bound, the remaining digits can only range from \(0\) to the corresponding digit of the upper bound. Compute contributions in two parts separately. Finally, consider leading zeros: when the \(i\)-th digit is a leading \(0\), the first \(1\) to \(\mathit{i-1}\) digits are also \(0\), meaning we overcounted the answer for filling the first \(i-1\) digits, which needs to be subtracted.
Implementation
Reference Code
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 | |
Method 2
Explanation
This problem can also be solved using memoized search. \(\mathit{dp}_i\) represents the answer for numbers with \(i\) digits when not tight to the upper bound and without leading zeros.
See code comments for details.
Process
Reference Code
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 | |
Example 2
Example 2 HDU 2089 Don't 62
Problem summary: Count how many numbers in an interval do not have the digit 4 nor the consecutive digits 62.
Explanation
For the constraint of no 4, simply skip 4 when enumerating digits to ensure state validity, so this constraint does not require memoization. For 62, since it involves two digits, the counting differs depending on whether the previous digit is 6 or not. Therefore, we need states to record different counts. \(\mathit{dp}_{\mathit{pos},\mathit{sta}}\) represents the current digit position \(\mathit{pos}\) and whether the previous digit is 6. Here \(\mathit{sta}\) only needs two states: 0 and 1. Cases where the previous digit is not 6 can be treated as the same, which does not affect counting.
Implementation
Reference Code
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 | |
Example 3
Example 3 SCOI2009 windy numbers
Problem summary: Given an interval \([l,r]\), count the numbers satisfying the condition: no leading zeros and the absolute difference between adjacent digits is at least 2.
Explanation
First, transform the problem into a simpler form. Let \(\mathit{ans}_i\) denote the count of numbers satisfying the condition in interval \([1,i]\). Then the desired answer is \(\mathit{ans}_r-\mathit{ans}_{l-1}\).
For a number less than \(n\), there must be some digit from high to low where its value is less than the corresponding digit in \(n\), while all previous digits equal those of \(n\).
Using this property, we can define \(f(i,st,op)\) as the number of numbers when considering the \(i\)-th digit from high to low, with current prefix state \(st\) and relation \(op\) to the number being solved (\(op=1\) means equal, \(op=0\) means less). In this problem, the prefix state is the value of the previous digit, because the digits that cannot be chosen for the current position depend only on the previous digit. In other problems, this value could be: the digit sum of the prefix, the \(\gcd\) of all digits in the prefix, the remainder of the prefix modulo some number, or a combination of two or more.
Write the state transition equation: \(f(i,st,op)=\sum_{k=1}^{\mathit{maxx}} f(i+1,k,op=1~ \operatorname{and}~ k=\mathit{maxx} )\quad (|\mathit{st}-k|\ge 2)\)
Here \(k\) is the value of the next digit being enumerated, and \(\mathit{maxx}\) is the maximum digit allowed at the current position. If \(\mathit{op}=1\), the digit chosen cannot exceed the corresponding digit of the number being solved; otherwise, there is no restriction.
We observe that even if the prefix states differ, as long as the three parameters of \(f\) are the same, the answer remains identical. To avoid computing this answer multiple times, we can implement it using memoized search.
Implementation
Reference Code
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 | |
Example 4
Example 4.SPOJMYQ10
Problem summary: If we write down all integers between \([n,m]\) by hand, how many numbers look exactly the same when viewed in a mirror? (\(n,m<10^{44}, T<10^5\))
Explanation
Note: Considering mirror images, only \(0,1,8\) are mirror-symmetric to themselves. Therefore, "look exactly the same" here does not mean traditional palindromes, but palindromes consisting only of \(0,1,8\).
First, during Digit DP, obviously only \(0,1,8\) can be selected.
Second, since the numbers exceed long long range, the formula \([n,m]=[1,m]-[1,n-1]\) is no longer applicable (high-precision comparison is cumbersome). Instead, we need to check whether \(n\) itself is valid, leading to: \([n,m]=[1,m]-[1,n]+\mathrm{check}(n)\).
Mirror symmetry is handled. How to check for palindrome?
We need a small array to record previous digits. Before exceeding half the length, we only need to ensure the digit does not exceed the upper bound. After exceeding half the length, we also need to check if the digit equals its "mirror-symmetric" counterpart.
An additional note: for memoization in this problem, memset cannot be used, as it would cause timeouts.
Implementation
Reference Code
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 | |
Example 5
Example 5.P3311 Count
Problem: We call a positive integer \(x\) lucky if and only if its decimal representation does not contain any string from set \(S\) as a substring. For example, when \(S = \{22, 333, 0233\}\), \(233233\) is lucky, while \(23332333\), \(2023320233\), \(32233223\) are not. Given \(n\) and \(S\), compute the number of lucky numbers not exceeding \(n\). Output the answer modulo \(10^9 + 7\).
\(1 \leq n<10^{1201},1 \leq m \leq 100,1 \leq \sum_{i = 1}^m |s_i| \leq 1500,\min_{i = 1}^m |s_i| \geq 1\), where \(|s_i|\) denotes the length of string \(s_i\). \(n\) has no leading zeros, but \(s_i\) may have leading zeros.
Explanation
Reading the problem, if we treat numbers as strings, this requires multi-pattern matching, naturally leading to AC automaton. In standard Digit DP, we enumerate digits from high to low, then enumerate which digit to fill at each position. In this problem, we naturally transform to enumerating the number of digits already filled, then enumerating which node we are currently at on the AC automaton, and transitioning from the current node to its children on the AC automaton.
Let \(f(i,j,0/1)\) represent that we have filled \(i\) digits from high to low (i.e., traversed \(i\) edges on the AC automaton), currently at node \(j\), and whether we are tight to the upper bound.
For the "does not contain" condition, simply mark the ending nodes of each pattern string on the AC automaton, and skip these ending nodes during DP.
The transitions are straightforward; see the main function part of the code.
Implementation
Reference Code
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 | |
This problem helps in understanding the principles of Digit DP well.
Exercises
Ahoi2009 self same distribution
Luogu P3413 SAC#1 - Cute Numbers
本页面最近更新:,更新历史
发现错误?想一起完善? 在 GitHub 上编辑此页!
本页面贡献者:OI-wiki
本页面的全部内容在 CC BY-SA 4.0 和 SATA 协议之条款下提供,附加条款亦可能应用