从1到n的所有整数的十进制表示中1出现的次数(扩展到任意数字)

private static int countOnes(int n) {
        int ones = 0;
        for(int m = 1; m <= n; m *= 10) {
                int a = n/m, b = n%m;
                ones += (a+8)/10*m + (a%10 == 1 ? b+1 : 0);
	}
	return ones;
}


思路:将n按位考虑,从个位到最高位,计算每一位上的1各出现几次,然后相加。

时间复杂度:O(log n)

举例:

n = 111
m a b ones加数因子
1 111 0 11+1
10 11 1 10+2
100 1 11 0+12

ones总共为36


拓展:从1到n的所有整数的十进制表示中一共有多少个k(k=1,2,...,9)

private static int countKs(int n, int k) {
        int ks = 0;
        for(int m = 1; m <= n; m *= 10) {
                int a = n/m, b = n%m;
                ks += (a+9-k)/10*m + (a%10 == k ? b+1 : 0);
        }
        return ks;
}

时间复杂度:O(log n)


k=0

这个情况比较复杂,需要分情况讨论

private static int countZerosDirectly(int n) {
        int zeros = 0;
        for(int m = 1; m <= n/10; m *= 10) {//最高位不用考虑
                int a = n/m, b = n%m;
                if(m > 1 && a%10 == 0)//表示n的高位(非个位)含0的情况,此时a/10不能直接乘乘数因子m,否则会多算
                        zeros += (a/10-1)*m + (b+1);
                else//其他情况正常计算,此时k=0的情况是比非0情况容易的
                        zeros += a/10*m;
        }
        return zeros;
}

时间复杂度:O(log n)


对于k=0的情况,正向思维不容易直接想出以上两种情况,这里也可以逆向思维,算出1到n这n个整数中0到9这10个数字总共出现多少次,减去1到9出现的总次数,就得到0出现的次数了(也可以用于验证正向思维结果)。

private static int countZerosIndirectly(int n) {
        int zeros = countN(n);
        for(int i = 1; i < 10; i++) {
                zeros -= countKs(n, i);
        return zeros;
}
	
private static int countN(int n) {
        int count = 0;
        int m = 1, k = 1;
        for(m = 1; m <= n/10; m *= 10) {
                count += 9*m*k;
                k++;
        }
        count += (n-m+1)*k;
        return count;
}

时间复杂度:O(log n),但显然代价大于直接法。

猜你喜欢

转载自blog.csdn.net/qq_26043397/article/details/80043121