Count of Integers

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。现在要求的是在[num1,num2]范围内的数字中,有多少个数字的各位之和在[max_sum,min_sum]范围内。为此我们可以先求出0num2中满足各位之和在[max_sum,min_sum]范围内的数字个数,记作b。再求出0num11中满足各位之和在[max_sum,min_sum]范围内的数字个数,记作a。那么答案就是ba

  而对于任意数字x,如何求出0x中满足各位之和在[max_sum,min_sum]范围内的数字个数呢?一样用到数位dp。先求出0x中各位之和不超过max_sum的数有多少个,记作a2。再求出0x中各位之和不超过min_sum1的数有多少个,记作a1。那么0x中满足各位之和在[max_sum,min_sum]范围内的数字个数就是a2a1

  因此最终答案就是(b2b1)(a2a1)

  定义状态f(i,j,k)表示所有长度为i,且最高位为数字j,各位之和不超过k的数的集合。根据次高位是哪个数字进行状态划分,因此状态转移方程就是f(i,j,k)=u=09f(i1,u,kj)

  这里用递推的方式来dp。现在求0num中各位之和不超过x的数有多少个。从最高位往底位枚举(从0开始),假设当前枚举到第i位数字num[i],并且第0i1位的数字之和为s,那么先累加所有长度为ni,第i位是0num[i]1且各位之和为xs的数的个数,即j=0num[i]1f(ni,j,xs)。以此类推。当在枚举的过程中发现s>x,那么直接返回答案即可。如果枚举完最后的sx,那么答案加1,因为num也满足要求。

  这样做的计算量为n×m×102,其中n是数字的位数,m是各位之和的最大值。如果这样做的话在leetcode会超时,估计卡常了。注意到n最大为23,因此m最大为23×9<400(所有的数都是9的情况),为此我们可以让m放小,令m=min{m,9×n},这样就可以减低计算量了,变成n2×103

  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 };
复制代码
posted @   onlyblues  阅读(13)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
Web Analytics
点击右上角即可分享
微信分享提示