您的位置:首页 > 编程语言 > Go语言

LeetCode Algorithms 233. Number of Digit One 题解

2017-06-05 13:20 190 查看
题目:Given an integer n, count the total number of digit 1 appearing in all non-negative integers less than or equal to n.

For example:

Given n = 13,

Return 6, because digit 1 occurred in the following numbers: 1, 10, 11, 12, 13.

题目意思就是求前n个非负整数里面出现1的次数

显然可以从0遍历到n,然后每个数字都求一次1的次数这样来解决,这是一个O(n)的解决方案,算不上很慢,但是看起来就觉得这是最差的方法。

通过观察可以发现,当我们按位数的个数来划分时,

位数是1时是
0-9
,1的个数为1,记为
a1


位数是2时是
10-99
,1的个数为
10+a1+8xa1=19
,记为
a2


位数是3时是
100-999
,1的个数为
100+a1+a2+8x(a1+a2)=280
,记为
a2


……

这里解释一下位数是2时是怎么数1的:

因为是数1,所以
10-19
首先包含十位数的10个1,数完这10个十位数的1后,把10-19看成
0-9
来数(不需要再数十位数的1),即a0,然后
20-99
,因为十位数都不是1,所以全部看成
0-9
来数,共8个a0,加起来就是
10+a0+8xa0=19


同理,位数是3时,100-199有100个1,然后看成0-99来数,这个刚刚已经数过了是a1+a0,而200-999,因为百位数不是1,全部看成0-99来数,共8x(a1+a0)。

因为是求和,用an做记号不方便,设Sm=a0+a1+……+am,其中a0表示n<0时,且a0=0,那么:

位数是1时,1的个数为
S1


位数是2时,1的个数为
S2


……

位数是m时,1的个数为
Sm


根据前面的观察,写出Sm的递推式子:

Sm=10^(m-1)+(a0+a1+...+a(m-1))+8x(a0+a1+...+a(m-1))+(a0+a1+...+a(m-1))


其中最后一项
(a0+a1+...+a(m-1))
表示加上位数更小的结果,这样才算完整的
0-999...99(m个9)
的1的个数。化简Sm后:

Sm=10^(m-1) + 10Sm-1


求得:

Sm=m10^(m-1)


到这里我们可以轻松的得到前n=999…999个非负整数的1的个数,显然这样是不够的,n可以是任意非负整数,比如n=23。面对这种情况,我们模仿Sm的推理过程,把23分成0-9,10-19,20-23这三段来计算,那么n=23时,1的个数={0-9}
S1
+{10-19}
10^1+S1
+{20-23}
(2-2)xS1+S1
=13,

可见需要用到每个位数上的数字,因此使用一个数组N保存每个位数上的数字,对于一个
m
位的
n


n=N[0]+10N[1]+...+10^(m-1)N[m-1]


从最高位开始,每一个位数以及后面的数字当成一段来计算,这样第
i
段的1的个数可以写为:

S(m-1)+10^(m-1)+(N[i]-1)xS(m-1)


计算完这段之后把该位置0,

这里需要注意两点:

1. 当N[i]=1时,不能直接计算10^(i-1),因为没有包含10…0-19…9中所有整数,这里
10^(m-1)+(N[i]-1)xS(m-1)
需要换成数字1后面的那串数字+1(比如123,就是换成23+1),然后进行下一段的计算。

2. 当N[i]=0时,应该跳过然后进行下一段的计算。

把以上算法写成代码如下:

int countDigitOne(int n) {
/* S(n) = 10 * (10^(n - 2) + S(n-1))
where S(n) is the sum of 1 of the number of n digits
and it follows that S(0) = 0 digits S(1) = 1 ... S(n) = n * 10^(n - 1)
*/
if (n <= 0) return 0;
if (n < 10) return 1;
int res = 0;
int tmp = n;
int digits = 0; // digits of n
vector<int> S; // Sn
vector<int> N; // N[i] represents the number of (i - 1) digit
while (tmp) {
N.push_back(tmp % 10);
tmp /= 10;
S.push_back(digits++ * pow(10, digits - 1));
}
while (!N.empty()) {
if (N.back() == 0) {
N.pop_back();
continue;
}
res += S[N.size() - 1];
if (N.back() > 1) {
int m = N.size() - 1;
res = res + pow(10, m) + (N.back() - 1) * S[m]; // S[m] + (m - 2) * S[m]
n -= N.back() * pow(10, N.size() - 1);
}
else {
res = res + (n -= N.back() * pow(10, N.size() - 1)) + 1;
}
N.pop_back();
}
return res;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  leetcode