这个题从想思路到实现再到debug,前后差不多用了1个小时时间,不过是因为昨晚十二点多做的,在碰到一种情况时脑子有点不清醒耽误了点时间。
这题作为hard来说 我觉得有点不太够,自认为hard的题应该是那种算法设计的特别巧妙的题,我做这题更像是找规律,不过也可能是别的大神有严谨的
数学推导,还是自己太菜了。
找规律
范围 | 1的个数 | 计算方法 |
0-9 | 1 | 只有1 |
10 - 19 | 11 | 十位上的1出现了10次 0-9个位上出现了1次 |
0-99 | 20 | 0-9的1出现了1次 10 - 99的个位上的1循环了9次 10-19的十位上1循环了10次 所以0-99 一共出现 20次 1 1 + 9 * 1 + 10 = 20 即 10 * 1 + pow(10, 1) = 20 |
100 - 199 | 120 | 百位的1循环了100次 100 - 199 的各位十位出现了 20次1 |
0 - 999 | 300 | 0-99 出现了 20次1 从100 - 999的十位个位的1循环了9次0-99中1的个数 100 - 199 百位循环了100次 20 + 9*20 + 100 = 300 即 10 * 20 + pow(10, 2) = 300 |
0-9999 | 4000 | 10 * 300 + pow(10, 3) = 4000 |
规律找的差不多了,我们把0-9,0-99,0-999,0-9999这种的记为基数
- 那我们来看一个一般情况,比方说2567
2567 = 2000 + 500 + 60 + 7
对于2000 来说,出现1的个数是百位十位个位的0-999循环了2次,1000 - 1999千位的1循环了1000次,所以就是 2 * 300 + pow(10, 3) = 1600
对于500来说,出现1的个数是十位个位的0-99循环了5次,100-199的百位的1循环了100 次,所以是 5*20 + pow(10, 2) = 200
对于60来说,出现1的个数是个位的0-9循环了6次,10-19的十位循环了10次,所以是6*1 + pow(10, 1) = 16
对于7来说,出现1的个数是就是 7 * 0 + pow(10, 0) = 1
所以一共2567出现了1600 + 200 + 16 + 1 = 1817次
- 特殊情况,因为2567的每一位都大于1,所以我们2000时就认为千位的1循环了1000次,计算500时百位的1循环了100次,60十位的1循环了10次
但是比方134,当百位是1时,百位并没有循环了100次,而是只循环了35次(100 - 134),因此要分开计算
上代码:
//从构思到解答到debug花了将近一个小时,吐了
class Solution {
public:
int countDigitOne(int n) { // 3751
vector<int> bit; // 1573
bit.push_back(-1);
while(n) {
bit.push_back(n % 10);
n /= 10;
} //我们先把每一位都单独存出来
int count = 0; //记录1的总个数
vector<long> dp{0};
//用来记录基数,dp[0]对应0-9有1个1,dp[1]对应0-99有20个1,dp[2]对应0-999有300个1
for (int i = 1; i < bit.size(); ++i) { //遍历每一位,累加
if (bit[i]) { //当该位大于0时我们进行计算
if (bit[i] > 1) { //当该位>1时,比方300,我们可以放心的循环pow(10, 2)=100次
count += bit[i] * dp[i-1] + pow(10, i-1);
}
else { //当该位为1时,我们只能循环一部分,比方134,只能循环35次
int tmp = 0;
int j = i-1;
while (j > 0) {
tmp = tmp * 10 + bit[j--];
}
tmp += 1; //计算35
count += bit[i] * dp[i-1] + tmp;
}
}
//计算当前位的基数
dp.push_back(dp[i-1]*10 + pow(10, i-1));
}
return count;
}
};
到此就结束了,不知道有没有讲清楚,也没有严谨的推导,这笨办法就是找规律,所以我感觉不像是个hard的题