<LeetCode OJ> 357. Count Numbers with Unique Digits

通过动态规划解决LeetCode题目,计算给定范围内所有各位数字均不同的数的个数。利用递推公式f(k)=9*9*8*...(9-k+2)进行计算。

Total Accepted: 450  Total Submissions: 1077  Difficulty: Medium

Given a non-negative integer n, count all numbers with unique digits, x, where 0 ≤ x < 10n.

Example:
Given n = 2, return 91. (The answer should be the total numbers in the range of 0 ≤ x < 100, excluding [11,22,33,44,55,66,77,88,99])

Hint:

  1. A direct way is to use the backtracking approach.
  2. Backtracking should contains three states which are (the current number, 
  3. number of steps to get that number and a bitmask which represent which number is marked as visited so far in the current number). 
  4. Start with state (0,0,0) and count all valid number till we reach number of steps equals to 10n.
  5. This problem can also be solved using a dynamic programming approach and some knowledge of combinatorics.
  6. Let f(k) = count of numbers with unique digits with length equals k.
  7. f(1) = 10, ..., f(k) = 9 * 9 * 8 * ... (9 - k + 2) [The first factor is 9 because a number cannot start with 0].

Credits:
Special thanks to @memoryless for adding this problem and creating all test cases.

Subscribe to see which companies asked this question

Show Tags

分析与翻译:

给定一个非负整数 n,计算所有各位数字均不同的数字x的个数,其中0 ≤ x < 10^n。
例子:

给定n=2,答案为91.因为在0-100的数字中除了11,22,33,44,55,66,77,88,99其他所有数各位均不同。
提示:
1)一个直接的办法是使用回溯法。
2)回溯应当包含三个状态(当前数字,得到该数字需要的步数,以及一个比特掩码用来表示当前已经访问过哪些位)。从状态(0,0,0)开始,并计算直到10^n为止的有效数字个数。
3)这个问题也可以使用动态规划方法与一些组合的知识来求解。
4)定义子问题f(k) :长度为k时各位均不同数字的个数。
5)f(1) = 10, ..., f(k) = 9 * 9 * 8 * ... (9 - k + 2) [首个因数是9,因为数字不能从0开始].

分析开始:找规律

既然说可以用动态规划,并且子问题都给你定义好了,那么这个问题就简单了!

n=1时,一位数A,[0,10)显然10个,
n=2时,两位数AB,[0,100),第一位A有9种选择(不能为0),第二位B有9中选择,81种,再加一位的10种,共91种
n=3时,三位数ABC,[0,1000),同理,A有9种可能,B有9种,C有8种...9*9*8+81+10=739

...

所以f(k) = 9 * 9 * 8 * ... (9 - k + 2),我们所做工作只是累加f(1)到f(n)即可,分析完毕。

代码一目了然,不言而喻

class Solution {
public:
    int countNumbersWithUniqueDigits(int n) {
        int result=0;
        for(int i=1;i<=n;i++)
            result+=getfk(i);
        return result==0?1:result;
    }
    
    int getfk(int k)
    {
        if(k==1)
            return 10;
        int val=9;    
        for(int i=2;i<=k;i++)
            val*=(9-i+2);
        return val;
    }
};

简写代码:

class Solution {
public:
    int countNumbersWithUniqueDigits(int n) {
        if ( n < 0 )  
            return 0;
        int result = 1;
        int multiplier = 9;
        n = min(n, 10);
        for (int i = 1; i <= n; i++) {
            result += multiplier;
            multiplier *= (i > 9 ? 0: (10 - i));
        }
        return result;
    }
};





注:本博文为EbowTang原创,后续可能继续更新本文。如果转载,请务必复制本条信息!

原文地址:http://blog.youkuaiyun.com/ebowtang/article/details/51658886

原作者博客:http://blog.youkuaiyun.com/ebowtang

本博客LeetCode题解索引:http://blog.youkuaiyun.com/ebowtang/article/details/50668895

<think>我们需要将LeetCode模式改为ACM模式,即自己编写完整的程序,包括main函数来处理输入输出。 在ACM模式中,通常需要从标准输入(如键盘)读取数据,处理后再输出到标准输出(控制台)。 目要求:给定一个由整数组成的非空数组digits,表示一个非负整数。将该整数加一,并返回结果数组。 修改步骤: 1. 添加main函数,处理输入输出。 2. 输入格式:通常ACM竞赛中,输入可能有多行,但这里我们假设输入一行数字,代表数组digits(例如:1 2 3)。 3. 读取输入:使用getline读取一行,然后用字符串流分割成整数。 4. 调用Solution类的plusOne方法。 5. 输出结果:将返回的数组输出(空格分隔)。 注意:原函数plusOne有错误,因为它只加了最后一位,但没有处理进位。例如[9,9]加一后应该变成[1,0,0]。 因此,我们需要先修复plusOne函数,使其正确处理进位。 修复plusOne的思路: - 从最后一位开始加1,然后向前处理进位。 - 如果某一位加1后等于10,则置0并进位1(继续处理前一位)。 - 如果到了第一位还有进位,则在数组开头插入1。 修改后的plusOne函数: 但是目要求是修改为ACM模式,所以我们将修复函数并添加main函数。 修改后的代码:</think>要将LeetCode模式改为ACM模式,需要添加完整的输入输出处理逻辑。以下是修改后的代码,包含对输入的处理和进位逻辑的修复: ```cpp #include <iostream> #include <vector> #include <sstream> #include <string> using namespace std; class Solution { public: vector<int> plusOne(vector<int>& digits) { int carry = 1; // 初始进位为1(相当于加一) for (int i = digits.size() - 1; i >= 0; i--) { int sum = digits[i] + carry; digits[i] = sum % 10; // 当前位结果 carry = sum / 10; // 计算进位 if (carry == 0) break; // 无进位可提前终止 } // 处理最高位进位 if (carry > 0) { digits.insert(digits.begin(), carry); } return digits; } }; int main() { Solution sol; string input; // 读取整行输入 while (getline(cin, input)) { if (input.empty()) break; // 将字符串转换为整数数组 vector<int> digits; istringstream iss(input); int num; while (iss >> num) { digits.push_back(num); } // 执行加一操作 vector<int> result = sol.plusOne(digits); // 输出结果 for (int i = 0; i < result.size(); i++) { cout << result[i]; if (i < result.size() - 1) cout << " "; } cout << endl; } return 0; } ``` ### 关键修改说明: 1. **修复算法逻辑**: - 添加进位处理(从末位向首位遍历) - 处理最高位进位(如 `999 + 1 = 1000`) - 提前终止机制(无进位时停止循环) 2. **ACM输入处理**: - 使用 `getline(cin, input)` 读取整行输入 - 通过 `istringstream` 分割字符串为整数数组 - 支持多组测试数据(空行终止) 3. **输出格式**: - 数字间用空格分隔(如 `1 0 0 0`) - 每组结果单独一行 - 符合ACM竞赛标准输出格式 ### 使用示例: **输入**: ``` 1 2 3 9 9 ``` **输出**: ``` 1 2 4 1 0 0 ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值