leetcode 233. 数字1的个数(Number of Digit One)

给定一个整数 n,计算所有小于等于 n 的非负数中数字1出现的个数。

例如:

给定 n = 13,

返回 6,因为数字1出现在下数中出现:1,10,11,12,13。

思路:从数的最高位依次计算1出现的次数,对于每整位出现的次数可事先用备忘录记录,避免重复计算
例如:
1.数字150,先计算51-149出现1的次数,那么在百位上,出现的次数应该是49+1次(还有150也要算进去);
然后计算剩余两位,那么就是51-149之间出现的1的次数(忽略百位,已经计算过),这时候结果跟01-99是一样的:考察个位和十位,容易得出这两位1出现的次数都是10次,就可以用备忘录直接得出count(01-99)

2.如果首位数字大于1,例如250,那么count(1-250) = count(1-199) + count(1-50)
其中count(1-199)= 2*count(1-99) + 100 ,其中count(1-99)已经存在备忘录中,另外100次的来源是计算100-199时,百位出现了100次1

 static Map<Integer, Integer> map = new HashMap<Integer, Integer>();
    static{
        map.put(99, 20);
        map.put(199, 140);
        map.put(299, 160);
        map.put(999, 300);
        map.put(9999, 4000);
        map.put(99999, 50000);
        map.put(999999, 600000);//一百万
        map.put(9999999, 7000000);
        map.put(99999999, 80000000);//一亿
        map.put(999999999, 900000000);
        map.put(2000000000, 1899999999);//十亿零1到20亿
    }
    static int count = 0;
    public int countDigitOne(int n) {
        if(n < 1){
            return 0;
        }
        if(n < 100){
            return countDigitOneInN(n);
        }
        count = 0;
        String str = n+"";
        int len = str.length();
        while(len > 1){
            int pow = (int) Math.pow(10,len-1);
            int divNum = n / pow;
            if(divNum == 1){
                count += map.get(pow - 1) + n%(pow) + 1;
            }
            else{
                count += map.get(pow - 1) * (divNum) + (n-n%pow)/divNum;
            }
            n %= pow;
            if(n < 100){
                return count + countDigitOneInN(n);
            }
            str = n+"";
            len = str.length();
        }

        return count;
    }

    public int countDigitOneInN(int n) {

        int count = 0;
        for (int i = 1; i <= n; i++) {
            int temp = i;
            while (temp != 0) {
                if (temp % 10 == 1) {
                    count++;
                }
                temp /= 10;
            }

        }
        return count;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值