《编程之美》“1的数目”的另一个解法
问题:给定一个十进制正整数N,写下从1开始,到N的所有整数,然后数一下其中出现的所有“1”的个数f(N)。
书上给的最优解,考虑十进制表示的每一位,对于0,1,其他这三种情况分开讨论,然后结合高位数字、当前位数字、低位数字计算。
我想到的是另一个解法,时间复杂度一样,但思路更简单一些:
1 2 3 .... 10 11 12 .... 100 101 102 .... 1000 1001 1002
个位数的1:每10个有1个
十位数的1:每100个有10个
百位数的1:每1000个有100个
千位数的1:每10000个有1000个
...
逐位统计。例如百位数上的1,先看有几个整千,计算100*(N/1000)。然后考虑剩余的不到1000的部分,从100算起有几个(注意不到100的不算,大于等于200的也不算)。两部分加起来即可。
C++17代码实现如下:
#include <algorithm>
int f(int n) { int iCount = 0; int iFactor = 1; while (n / iFactor != 0) { int iFactorH = iFactor * 10; iCount += iFactor * (n / iFactorH) + std::clamp(n % iFactorH - iFactor + 1, 0, iFactor); iFactor *= 10; } return iCount; }
结果验证过是一致的。
顺便打印了一下所有f(N)=N的案例:
1 199981 199982 199983 199984 199985 199986 199987 199988 199989 199990 200000 200001 1599981 1599982 1599983 1599984 1599985 1599986 1599987 1599988 1599989 1599990 2600000 2600001 13199998 35000000 35000001 35199981 35199982 35199983 35199984 35199985 35199986 35199987 35199988 35199989 35199990 35200000 35200001 117463825 500000000 500000001 500199981 500199982 500199983 500199984 500199985 500199986 500199987 500199988 500199989 500199990 500200000 500200001 501599981 501599982 501599983 501599984 501599985 501599986 501599987 501599988 501599989 501599990 502600000 502600001 513199998 535000000 535000001 535199981 535199982 535199983 535199984 535199985 535199986 535199987 535199988 535199989 535199990 535200000 535200001 1111111110
并不是很多,而且可以看到有抱团的现象。书中证明了这个集合是有限的,最大值就是1111111110。