数字1的个数
今天是小安开始Leetcode刷题的第233题,正文开始ing?
题目描述
给出一个 32 位的有符号整数,你需要将这个整数中每位上的数字进行反转。题目原址
示例
输入: 13
输出:6
解释:数字 1 出现在以下数字中: 1, 10, 11, 12, 13 。
方法:暴力法
问题转化
求最高位对1的贡献
例如输入193,我们先求1作为最高位百位对1的贡献,假设结果为c1;然后我们将最高位1去掉,就剩下93,我们求得9作为最高位十位对1的贡献为c2;同样我们去掉9,剩下3,我们求得3作为最高位个位对1的贡献是c3。
答案 = c1+c2+c3。
那么,求最高位对1的贡献怎么求?按照以下规则:
如果n是一位数,最高位就是个位,由于没有比它更小的位数了,因此对1的贡献只有它本身(个位),如果各位数字大于等于1,贡献是1,否则贡献是0。
如果n是两位数,最高位就是十位,因此对1的贡献分为0-9部分和十位本身。0-9:贡献能力是1,再乘以十位数字就是贡献1的个数;十位:贡献大小取决于十位数字,如果十位数字大于1,则贡献是10;如果十位数字等于1,则贡献是去掉十位剩下的数+1。
如果n是三位数,最高位就是百位,因此对1的贡献分为0-99部分和它本身(百位)。0-99:贡献能力是20,百位:贡献大小取决于百位数字,如果百位数字大于1,则贡献是100;如果十位数字等于1,则贡献是去掉百位剩下的数+1。
如果n是四位数,最高位就是千位,因此对1的贡献分为0-999部分和它本身(千位)。0-999:贡献能力是300,千位:贡献大小取决于千位数字,如果千位数字大于1,则贡献是1000;如果千位数字等于1,则贡献是去掉千位剩下的数+1。
以此类推。。。。。。
举例说明
413是个三位数,我们这里先考虑最高位4对1的贡献,根据上面的规则,贡献分为0-99部分和百位部分。首先说0-99贡献:0-99共有20个1,因此贡献能力是20,一共有四次(0-99、100-199、200-299、300-399),因此共贡献了420 = 80个1;再说百位的贡献:因为百位数字是4,大于1,因此百位的贡献就是100(100、101、…、199一共100个 1)。因此,413最高位4对1的贡献 = 420+100 = 180。
你可能会问,这里只考虑最高位4对1的贡献,后面的13也贡献了6个1。没错,因此我们考虑完4对1的贡献后,就要将4剔除,剩下13,我们还是套用此规则。
13是个两位数,我们这里只找最高位1对1的贡献,根据上面的规则,贡献分为0-9部分和十位部分。首先说0-9贡献:0-9共有1个1,因此贡献能力是1,一共有1次(0-9),因此共贡献了11 = 1个1;再说十位的贡献:因为十位数字等于1,因此十位的贡献就是4(去掉十位后的数+1,即3+1,因为10、11、12、13)。因此,13最高位十位1对1的贡献 = 11+4 = 5。
然后剔除十位1,只剩下3了,3大于等于1,所以贡献是1。
因此结果 = 420+100 + 11+4 + 1 = 186。
0-9贡献能力是1,0-99贡献能力是20,0-999贡献能力是300,0-9999贡献能力是4000,这是怎么来的?
1 ∗ 10 + 10 = 20 ; 20 ∗ 10 + 1 0 2 = 300 ; 300 ∗ 10 + 1 0 3 = 400 1*10+10=20;20*10+10^2=300;300*10+10^3=400 1∗10+10=20;20∗10+102=300;300∗10+103=400。
代码实现
【java实现】
class Solution {
public int countDigitOne(int n) {
if (n < 1)
return 0;
int len = getLenOfNum(n);
if (len == 1)
return 1;
int tmp = (int) Math.pow(10, len - 1);
int first = n / tmp; // 获取n的最高位数字
int firstOneNum = first == 1 ? n % tmp + 1 : tmp; // 获取n的最高位为1时有多少个数字
int otherOneNUm = first * (len - 1) * (tmp / 10); // 在介于n % tmp到n之间的数字中,除了最高位为1,其余各个数字分别为1 的总数和
return firstOneNum + otherOneNUm + countDigitOne(n % tmp);
}
private int getLenOfNum(int n) {
int len = 0;
while (n != 0) {
len++;
n /= 10;
}
return len;
}
}