-----------------------------------2021/1/25二刷---------------------------------------
具体每一位的思路并不难,只要能写出0的情况就能顺理成章写出1和2~9的情况,
难点在于边界判定和一系列数字的计算。
所以记录一下
边界: cur = 0且 hNum = 0
单纯cur = 0绝对不行的:刚遇到hNum = 0说明进入最后一次计算。
数字变化:
一共有四个数字
lNum、hNum、cur、digit
变化顺序:
首先我们观察到cur = hNum /10
, hNum = hNum /= 10
所以cur的变化必须在hNum之前。
然后lNum = lNum + cur * digit
,digit = digit * 10
所以lNum的变化必须在cur和digit前
所以可得改变顺序:1. lNum 2.cur 3.digit和hNum随意
lNum += cur * digital;
//当前位置往前移动一位
cur = hNum % 10;
//截掉最低位
hNum /= 10;
//位权重乘10
digital *= 10;
------------------------------------一刷---------------------------------------------------
题目描述
一道找规律的困难题。一开始看到的时候,马上想当然0 ~ 9,10 ~ 99,100 ~ 999里面有几个1这样的思路去分析,但是这题的思路应该是对数字的每个位数来考虑出现1的次数。
对于一个位数cur 设为第wCur位,其出现1的次数分为以下三种情况来讨论。
- cur为0
我们设cur左边的位数构成的数字为 hNum,10^wCur为digital
则 wCur出现1的次数为 hNum * digital。
eg 2205 ,cur = 0 ,wCur = 2 ,由于0 ~ 21 一共能给wCur位创造 21 - 0 + 1 次出现的机会;
而wCur 本身作为第二位,对于每个机会能重复10^wCur次 比如对于第21次机会:2010 2011 2012 … 2019 - cur为1
设cur左边的位数构成的数字为hNum,右边的位数构成的数字为lNum
则wCur出现1的次数为 hNum * digital+ lNum + 1
eg 2215 ,cur = 1 , wCur = 2 , 除去第一种情况给予的机会;高位为 22 多创造的机会被重复的次数为 lNum + 1 即六次 :2210 2211 …2215 - cur大于1小于9
我们设cur左边的位数构成的数字为 hNum,10^wCur为digital
则 wCur出现1的次数为 hNum+1 * digital。
eg 2225 , cur = 2 ,wCur = 2,0 ~ 22一共能给wCur位创造 22- 0 + 1 次出现的机会; 而wCur 本身作为第二位,对于每个机会能重复10^wCur次
那么接下来就是如何进入每一位从而得到各位出现1次数之和的过程。
我们从各位开始一直到最高位
初始值
hNum = n/10;
lNum = 0;
cur = n%10;
digital = 1;
递归公式
//低位数字先计算当前位置数字大小
INum += cur * digital;
//当前位置往前移动一位
cur = hNum % 10 ;
//截掉最低位
hNum / =10;
//位权重乘10
digital *= 10;
结束条件
hNum == 0 && cur == 0
这里为什么两者都要为0呢,可能有人会认为hNum为0即可,但是当我们处在最高位的时候hNum为0,同样需要再计算一次,当且仅当最后一次计算结束之后,两者才会都为0。
特殊情况
因为题目会到达int的上限值,所以digital在计算的过程中可能越界,应该把其数据类型设为long
得到代码
class Solution {
public:
int countDigitOne(int n) {
int hNum = n / 10;
int lNum = 0;
long digital = 1;
int cur = n % 10;
int ans = 0;
int res;
while(cur || hNum) {
switch(cur) {
case 0: res = hNum * digital;
break;
case 1: res = hNum * digital + lNum + 1;
break;
default: res = (hNum + 1) * digital ;
break;
}
ans += res;
//低位数字先计算当前位置数字大小
lNum += cur * digital;
//当前位置往前移动一位
cur = hNum % 10;
//截掉最低位
hNum /= 10;
//位权重乘10
digital *= 10;
}
return ans;
}
};
时间复杂度O(logN)
空间复杂度O(1)