第二题,中等难度,看到正确率略小于0.2就做了
233 | Number of Digit One | 19.5% | Medium |
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是int类型的话,取接近最大值是必然会溢出的,所以leetcode的测试集还是手下留情了。
比如取N=20亿(2^31-1略大于21亿),1~10亿共会出现9亿+1个‘1’(0~9 9999 9999没位1/10几率出现‘1’共 【9位*(1/10)*10亿个数】,再加上10亿零头的一个‘1’)
然后10亿~20亿 == (10+0)亿~(10+10亿),每个数的十亿位都有一个‘1’,再加上个位到亿位9亿+1
有28亿个‘1’溢出了(去掉为方便说明的零头1,重复计数了)
System.out.println(nd.getNum(2000000000)-2000000000); 输出结果为8亿整,正确
(剧透思路了。。)
==========================================================
最简单的思路是遍历1~n,累加每个数字中出现的‘1’的次数。
毫无疑问,这样做肯定会超时。但是!这个方法却可以生成N的测试集!但凡是正确的解决方案,都有一定的价值。
在可以自己生成测试集的情况下,答题正确率不到0.2,可见具体过程还是有些小坑,且刷题人都是随便一刷
public int getNum_basic(int n){
if(n == 0) return 0;
int count = 0;
for(int i=1;i<=n;i++){
for(int j=i;j>0;j/=10){
if(j%10 == 1) count++;
}
}
return count;
}
==========================================================
优化思路,转化为数值计算。
例如 N=333,从百位上看,经历了4个过程:【000~099】【100~199】【200~299】【300~333】
诶!前几个过程是重复的耶!
所以,前三个过程可以直接计算‘1’的次数为 3*20 (个位十位分别出现10次1;同理从千位来看,个位十位百位分别出现10^2次1,如果N=3333,0~2999共出现3*300次‘1’)
(记得加上百位的1,共100个)
剩下的300~333,不是跟0~33出现‘1’的次数一样么。
最终求 0/1~333 的问题转化为求 1~300 + 1~30 + 1~3 的问题。
其中有个特殊情况是某位为1,比如33133
转化为 30000 + 3000 +133 后,133百位的1并没有出现100次,只有0~33共34次。
至于某位为0的情况,跳过此位即可
最终的算法,可以从高位或低位开始扫描都OK,
我选择的是低位开始,因为每次循环取位N%10后N/=10,取数字方便;缺点是要用一个变量来记录已扫描的值record,以便在出现某位为1的情况时得到该位1的次数。
高位开始的话,一开始需要计算总共位数bit(不是2进制bit),且最好直接计算一个tens值记录10^bit,每次循环tens/=10;这样就不用记录已扫描的值。
代码:
public int getNum(int n){
if(n<1) return 0;
int count = 0;
int bit = 1;
int tail = 1;
int tens = 1;
int record = 0;
for(int i=n;i>0;i/=10){
int baseCount = (bit-1)*(tens/10); //1234 4-0*0 3-1*1 2-2*10 1-3*100
bit++;
tail = i % 10;
if(tail == 1){
count += record+1 + baseCount;
}else if(tail > 1){
count += tens + baseCount*(tail);//1xx && 1/2/3/后的xx中的1
}
record += tail * tens; //1234 4 34 234 1234
tens *= 10;
}
return count;
}
运行结果:
第一次用java刷OJ,之前都是用C,即使是主要用其他语言工作的时候(说得好像有用C工作过一样),现在觉得还是慢点的好。
C码结果是这样的:|。