Count of Integers
You are given two numeric strings num1 and num2 and two integers max_sum and min_sum. We denote an integer x to be good if:
- num1 <= x <= num2
- min_sum <= digit_sum(x) <= max_sum.
Return the number of good integers. Since the answer may be large, return it modulo 109 + 7.
Note that digit_sum(x) denotes the sum of the digits of x .
Example 1:
Input: num1 = "1", num2 = "12", min_num = 1, max_num = 8 Output: 11 Explanation: There are 11 integers whose sum of digits lies between 1 and 8 are 1,2,3,4,5,6,7,8,10,11, and 12. Thus, we return 11.
Example 2:
Input: num1 = "1", num2 = "5", min_num = 1, max_num = 5 Output: 5 Explanation: The 5 integers whose sum of digits lies between 1 and 5 are 1,2,3,4, and 5. Thus, we return 5.
Constraints:
- 1 <= num1 <= num2 <= 1022
- 1 <= min_sum <= max_sum <= 400
解题思路
很经典的数位dp。现在要求的是在$[\text{num1}, \text{num2}]$范围内的数字中,有多少个数字的各位之和在$[\text{max_sum}, \text{min_sum}]$范围内。为此我们可以先求出$0 \sim \text{num2}$中满足各位之和在$[\text{max_sum}, \text{min_sum}]$范围内的数字个数,记作$b$。再求出$0 \sim \text{num1}-1$中满足各位之和在$[\text{max_sum}, \text{min_sum}]$范围内的数字个数,记作$a$。那么答案就是$b-a$。
而对于任意数字$x$,如何求出$0 \sim x$中满足各位之和在$[\text{max_sum}, \text{min_sum}]$范围内的数字个数呢?一样用到数位dp。先求出$0 \sim x$中各位之和不超过$\text{max_sum}$的数有多少个,记作$a_2$。再求出$0 \sim x$中各位之和不超过$\text{min_sum} - 1$的数有多少个,记作$a_1$。那么$0 \sim x$中满足各位之和在$[\text{max_sum}, \text{min_sum}]$范围内的数字个数就是$a_2 - a_1$。
因此最终答案就是$(b_2 - b_1) - (a_2 - a_1)$。
定义状态$f(i,j,k)$表示所有长度为$i$,且最高位为数字$j$,各位之和不超过$k$的数的集合。根据次高位是哪个数字进行状态划分,因此状态转移方程就是$$f(i,j,k) = \sum\limits_{u=0}^{9}{f(i-1,u,k-j)}$$
这里用递推的方式来dp。现在求$0 \sim \text{num}$中各位之和不超过$x$的数有多少个。从最高位往底位枚举(从$0$开始),假设当前枚举到第$i$位数字$\text{num}[i]$,并且第$0 \sim i-1$位的数字之和为$s$,那么先累加所有长度为$n-i$,第$i$位是$0 \sim \text{num}[i] - 1$且各位之和为$x - s$的数的个数,即$\sum\limits_{j=0}^{\text{num}[i]-1}{f(n-i,j,x-s)}$。以此类推。当在枚举的过程中发现$s > x$,那么直接返回答案即可。如果枚举完最后的$s \leq x$,那么答案加$1$,因为$\text{num}$也满足要求。
这样做的计算量为$n \times m \times 10^2$,其中$n$是数字的位数,$m$是各位之和的最大值。如果这样做的话在leetcode会超时,估计卡常了。注意到$n$最大为$23$,因此$m$最大为$23 \times 9 < 400$(所有的数都是$9$的情况),为此我们可以让$m$放小,令$m = \min \{ m, 9 \times n \}$,这样就可以减低计算量了,变成$n^2 \times 10^3$。
AC代码如下:
1 class Solution { 2 public: 3 int mod = 1e9 + 7; 4 5 int count(string num1, string num2, int min_sum, int max_sum) { 6 function<int(string, int)> get = [&](string s, int x) { 7 if (s == "0") return 1; // 特判0的情况 8 int n = s.size(); 9 x = min(x, 9 * n); 10 vector<vector<vector<int>>> f(n + 1, vector<vector<int>>(10, vector<int>(x + 1))); 11 for (int i = 0; i <= 9; i++) { 12 for (int j = i; j <= x; j++) { 13 f[1][i][j]++; 14 } 15 } 16 for (int i = 2; i <= n; i++) { 17 for (int j = 0; j <= 9; j++) { 18 for (int k = j; k <= x; k++) { 19 for (int u = 0; u <= 9; u++) { 20 f[i][j][k] = (f[i][j][k] + f[i - 1][u][k - j]) % mod; 21 } 22 } 23 } 24 } 25 int ret = 0, sum = 0; 26 for (int i = 0; i < n; i++) { 27 int t = s[i] - '0'; 28 for (int j = 0; j < t; j++) { 29 ret = (ret + f[n - i][j][x - sum]) % mod; 30 } 31 sum += t; 32 if (sum > x) break; // 前面各位之和已经超过x 33 } 34 if (sum <= x) ret++; // num满足各位之和不超过x 35 return ret; 36 }; 37 // 因为要求num1-1,因此对num1进行减1 38 reverse(num1.begin(), num1.end()); 39 for (int i = 0; i < num1.size(); i++) { 40 if (num1[i] - 1 >= '0') { // 不需要借位 41 num1[i]--; 42 break; 43 } 44 num1[i] = '9'; // 借位 45 } 46 while (num1.size() > 1 && num1.back() == '0') { // 把前导0删除,同时要至少保留一位数 47 num1.pop_back(); 48 } 49 reverse(num1.begin(), num1.end()); 50 return ((get(num2, max_sum) - get(num2, min_sum - 1) + mod) % mod - (get(num1, max_sum) - get(num1, min_sum - 1) + mod) % mod + mod) % mod; 51 } 52 };标签:Count,Integers,num1,min,text,sum,num,max From: https://www.cnblogs.com/onlyblues/p/17455885.html