20221018 - 902. Numbers At Most N Given Digit Set 最大为 N 的数字组合(数位动态规划)

85 阅读1分钟

Given an array of digits which is sorted in non-decreasing order. You can write numbers using each digits[i] as many times as we want. For example, if digits = ['1','3','5'], we may write numbers such as '13', '551', and '1351315'.

Return the number of positive integers that can be generated that are less than or equal to a given integer n.

Example 1

Input: digits = ["1","3","5","7"], n = 100
Output: 20
Explanation: 
The 20 numbers that can be written are:
1, 3, 5, 7, 11, 13, 15, 17, 31, 33, 35, 37, 51, 53, 55, 57, 71, 73, 75, 77.

Example 2

Input: digits = ["1","4","9"], n = 1000000000
Output: 29523
Explanation: 
We can write 3 one digit numbers, 9 two digit numbers, 27 three digit numbers,
81 four digit numbers, 243 five digit numbers, 729 six digit numbers,
2187 seven digit numbers, 6561 eight digit numbers, and 19683 nine digit numbers.
In total, this is 29523 integers that can be written using the digits array.

Example 3

Input: digits = ["7"], n = 8
Output: 1

Constraints

  • 1 <= digits.length <= 9
  • digits[i].length == 1
  • digits[i] is a digit from '1' to '9'.
  • All the values in digits are unique.
  • digits is sorted in non-decreasing order.
  • 1 <= n <= 1e9

Solution

官方题解:

本题为典型的数位动态规划题目,可以阅读「数位 DP」详细了解。我们称满足 xnx \le n 且仅包含 digits\textit{digits} 中出现的数字的 xx 为合法的,则本题需要找出所有合法的 xx 的个数。

nn 是一个十进制的 kk 位数,所有数字位数小于 kk 且由 digits\textit{digits} 组成的数字则一定是小于 nn 的。我们用 dp[i][0]\textit{dp}[i][0] 表示由 digits\textit{digits} 构成且 nn 的前 ii 位的数字的个数,dp[i][1]dp[i][1] 表示由 digits\textit{digits} 构成且等于 nn 的前 ii 位的数字的个数,可知 dp[i][1]\textit{dp}[i][1] 的取值只能为 0011

例如:n=2345,digits=[“1",“2",“3",“4"]n = 2345, \textit{digits} = \text{[``1",``2",``3",``4"]}

dp[1][0],dp[2][0],dp[3][0],dp[4][0]\textit{dp}[1][0], \textit{dp}[2][0], \textit{dp}[3][0], \textit{dp}[4][0] 分别表示小于 2,23,234,23452, 23, 234, 2345 的合法数的个数,dp[1][1],dp[2][1],dp[3][1],dp[4][1]\textit{dp}[1][1], \textit{dp}[2][1], \textit{dp}[3][1], \textit{dp}[4][1] 分别表示等于 2,23,234,23452, 23, 234, 2345 的合法数的个数。

digits\textit{digits} 中的字符数目为 mm 个,数字 nn 的前 jj 位构成的数字为 num[j]\textit{num}[j],数字 nn 的第 jj 个字符为 s[j]s[j],当遍历到 nn 的第 ii 位时:

  • 当满足 i>1i > 1 时,此时任意数字 dd 构成的数字一定满足 d<num[i]d < \textit{num}[i]
  • 设数字 a<num[i1]a < \textit{num}[i-1],则此时在 aa 的末尾追加一个数字 dd 构成的数为 a×10+da \times 10 + d,此时可以知道 dd0,1,,90,1,\cdots,9 中任意数字均满足小于 a×10+d<num[i]=num[i1]×10+s[i]a \times 10 + d < \textit{num}[i] = \textit{num}[i-1] \times 10 + s[i]
  • 设数字 a = \textit{num}[i-1]a=num[i−1],则此时在 aa 的末尾追加一个数字 dd 构成的数为 a \times 10 + da×10+d,此时可以知道 d < s[i]d<s[i] 时,才能满足 a \times 10 + d < \textit{num}[i] = \textit{num}[i-1] \times 10 + s[i]a×10+d<num[i]=num[i−1]×10+s[i];
  • 初始化时令 dp[0][1]=1\textit{dp}[0][1] = 1,如果前 ii 位中存在某一位 jj ,对于任意数字 dd 均不能满足 d=s[j]d = s[j],则此时 dp[i][1]=0\textit{dp}[i][1] = 0

根据上述描述从小到到计算 dpdp,设 C[i]C[i] 表示数组 digits\textit{digits} 中小于 nn 的第 ii 位数字的元素个数,则状态转移方程为:

dp[i][0]={C[i],i=1m+dp[i1][0]×m+dp[i1][1]×C[i],i>1dp[i][0] = \begin{cases} C[i], & i = 1 \\ m + dp[i-1][0] \times m + dp[i-1][1] \times C[i], & i > 1 \\ \end{cases}

我们计算出前 kk 位小于 nn 的数字的个数 dp[k][0]\textit{dp}[k][0],前 kk 位等于 nn 的数字的个数 dp[k][1]\textit{dp}[k][1],最终的答案为 dp[k][0]+dp[k][1]\textit{dp}[k][0] + \textit{dp}[k][1]

作者:LeetCode-Solution 链接:leetcode.cn/problems/nu… 来源:力扣(LeetCode)

int atMostNGivenDigitSet(char ** digits, int digitsSize, int n){
    int i, j, bits, dp[11][2] = {0};
    char s[11];
    sprintf(s, "%d", n);
    bits = strlen(s);
    dp[0][1] = 1;
    for (i = 1; i <= bits; i++) {
        for (j = 0; j < digitsSize; j++) {
            if (digits[j][0] == s[i - 1]) {
                dp[i][1] = dp[i - 1][1];
            } else if (digits[j][0] < s[i - 1]) {
                dp[i][0] += dp[i - 1][1];
            } else {
                break;
            }
        }
        if (i > 1) dp[i][0] += digitsSize + dp[i - 1][0] * digitsSize;
    }
    return dp[bits][0] + dp[bits][1];
}

题目链接:902. 最大为 N 的数字组合 - 力扣(LeetCode)