leetcode 357 计算各个位数不一样的数字个数

题目

leetcode 357 计算各个位数不一样的数字个数
给定一个非负整数 n,计算各位数字都不一样的数字 x 的个数,其中 0 ≤ x < 10n 。c++

示例:git

输入: 2
输出: 91
解释: 答案应为除去 11,22,33,44,55,66,77,88,99 外,在 [0,100) 区间内的全部数字。web

分析

  • 注意到 n 是非负整数,当n == 0,x = 1
  • 最高位的解空间是[1-9],其他位的解空间是[0-9]

暴力穷举(回溯)

  • 每一位都是路径中的节点
  • 解空间:第一个节点是[1-9],其余节点是[0-9]
  • 终止条件:路径长度 大于 n
  • 解的判断条件:每次进入函数时,counter加一,表示以当前节点为个位的不相同数
  • 回溯的状态:创建一个used数组,表示到当前节点时,已经使用过的数字,若是已经使用,直接结束;不然在解空间中选择一个未使用的进入下个节点搜索

code

class Solution {
    int counter = 0;
    vector<bool> used = vector<bool>(10,false);
    void backtrace(int n,int level){
    	counter++;
        if(level == n){
            return;
         }
        
        for(int i = level ? 0 : 1; i < 10; ++i){
            if(used[i]) continue;
            used[i] = true;
            backtrace(n,level + 1);
            used[i] = false;
        }
    }
public:
    int countNumbersWithUniqueDigits(int n) {
        if(n == 0) return 1;
        backtrace(n,0);
        return counter;
    }
};
  • 穷举的效率很低

动态规划

通常求取符合某种规则的的“串”的数量,可是不要求具体的“串”的集合,均可以用dp。数组

  • 定义dp[i]为,个数为i的不重复的数的个数。
  • dp[0] = 1;
  • dp[1] = 9;
  • dp[2] = 9 * 9; 十位能够选[1-9]中的任意一个,9种选择,个位选[0-9]中除去十位选择的任意一个,共 10 -1 = 9 种
  • dp[3] = 9 * 9 * 8;
  • dp[10] = 9 * 9 * 8 * 7 * 6 * 5 * 4 * 3 * 2 * 1
  • 要求的结果就是i:dp[0] + dp[1] + … + dp[n];
  • 根据以上规律,创建一个辅助table[] = {1,9,9,8,7,6,5,4,3,2,1}
  • 注意到dp[i] = dp[i -1] * table[i],所以用变量dp_i 来表示dp数组,节省空间。

code

class Solution {
public:
    int countNumbersWithUniqueDigits(int n) {
        vector<int> table{1,9,9,8,7,6,5,4,3,2,1};
        n =  n >=10 ? 10 : n; //n 大于10后是没有符合的数
        int counter = table[0];
        int  dp_i = 1;
        for(int i = 1; i <= n;++i ){
            dp_i *= table[i];
            counter += dp_i;
        }
        return counter;
    }
};