lintCode题解(3)

标签(空格分隔): lintCode


题目: 统计数字

描述:

计算数字k在0到n中的出现的次数,k可能是0~9的一个值

样例

例如n=12,k=1,在 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12],我们发现1出现了5次 (1, 10, 11, 12)

解析

思路一:暴力求解法

从0遍历到n,将其中的每一个数的各个位分别于k进行对比,如果和k相等,则计数加一。

int digitCounts(int k, int n) {
    int result = 0;
    for (int i = k; i <= n; i++) {
        int temp = i;
        while (temp > 9){
            int r = temp % 10;
            if (r == k) {
                result++;
            }
            temp /= 10;
        }
        if (temp == k) {
            result++;
        }
    }
    return result;
}

思路二:数学优化
我们看到这种方法的一个巨大的缺点是,当n越来越大时,我们需要分解的数,和步骤也越来越多,同时,我们发现我们分解了很大一部分得不到结果的数。这其实是一种浪费。

优化算法的原则:

  • 尽可能的滤掉不符合条件的步骤。
  • 尽量避免计算步骤随着n的增大,增长的过快。

基于这两个原则,如果能减少遍历次数,将会减少很大一部分运算量。最理想的情况下,如果我们指根据最后一个n能得出结果,这个就是最好的方式。分析整个题目,我们发现全部数字是处于一种递增的关系,也就是n其实是和总体之间是存在线性关联的,也就是说我们可以从n得出所有符合条件的数。

首先我们简化:

分析n = 10以内(不包括10)的数字,0,1,2,...,9,对任意一个数,总会出现一次,这里我们为了更加简化,我们先把0去掉,我们分析6.

分析100以内(去掉100),个位上的没10个就会出现一个6,分别是6,16,26,36,..,66,..,86,96;也就是个位上没个10个数就会出现一次,同时我们也看到十位上也出现了一次6就是60开始,我们发现60,61,62,63..,66,..,69,我们发现出现了10次,有人说66出现了两次,是不是该去掉,答案是不用,因为66本身其实就表示出现了两次6,

分析n = 300(去掉300)以内的数,个位数出现6的次数分别是6,16..,96,..,106,....,196..,206,..,296。每个10个出现一次,一共出现了300/10= 30次,十位上呢?分别是60,61..69,161..169,261..269;我们发现每隔100出现一次,出现3次,十位上每出现一次,机会增加10,共30次,则300以内6的个数为30+30 = 60次。

我们还发现一种情况:当n=900时,范围数值内出现了600-699的数值,这样会额外的增加100。我们需要进行修正。

依次类推,发现如果数字再打,则百位上出现一次6是每个1000出现一次,一次会增加100个。

于是我们可以这样理解:假设我们的n都是100,200,300,..1000,2000,9000,这样整百整千的数,我们是否可以写出代码呢?

显然,这个是很容易的。
我们需要一个条件,就是先知道这个数一共有多少位,然后根据位数来进行运算

int digitCounts(int k, int n) {
    int result = 0; //结果
    int base = 1;//最少是一位数
    int tmp = n; //n的副本
    int factor = 1;
    //计算一共有多少位
    while (tmp > 9){ 
        base++;
        tmp /= 10;
        factor = factor * 10;
    }
    //计算最高位
    int maxHigh = n / factor;
    //从个位到十位,到百位...,累计出现的次数
    //个位上出现一次就计数加1,十位上出现一次计数加10...
    //计数倍数
    int mulriple = 1;

    for (int i = 1; i < base; i++) {
        //计算在个位,十位,百位...出现的次数
        //个位,每隔10个数,出现一次
        int tFactor = 10 * mulriple;
        //总共出现多少次
        int time = factor/tFactor;
        result = result + maxHigh * time * mulriple;
        mulriple *= 10;
    }
    //修正结果,如果最高位大于k,则增加当前位进制大小的个数,
    //如900之于6,存在一个600,则统计中增加了一个100,即factor
    if (maxHigh > k) {
        result += factor;
    }
}

这样我们就可以计算n为整百整10的情况了,显然这样还够.还有一些其他的情况,如果n = 300,k = 3,我们没有把300计算进去,因此我们需要进去

int digitCounts(int k, int n) {
   ...//接着上面的
   if (maxHigh == k) {
        result = result +1;
    }
}

这样我们就把整百的考虑完成了,下面我没考虑费整百的情况如123,353,..,这种情况。
我们的思路是这样,例如353为例,我们先考虑300以内的,然后加上300-353的。300以内的上面已经完成了,300-353的该如何做呢?当k > 最高位时,其实也就看看353-300 = 53 中有多少个满足条件的. k < 最高位时,我们上面已经考虑进去了,当 k = 最高位时,我们只需要将外的53在加上即可。因此我们的程序可以这样写。

int digitCounts(int k, int n) {
    int result = 0; //结果
    int base = 1;//最少是一位数
    int tmp = n; //n的副本
    int factor = 1;
    //计算一共有多少位
    while (tmp > 9){ 
        base++;
        tmp /= 10;
        factor = factor * 10;
    }
    //计算最高位
    int maxHigh = n / factor;
    //从个位到十位,到百位...,累计出现的次数
    //个位上出现一次就计数加1,十位上出现一次计数加10...
    //计数倍数
    int mulriple = 1;

    for (int i = 1; i < base; i++) {
        //计算在个位,十位,百位...出现的次数
        //个位,每隔10个数,出现一次
        int tFactor = 10 * mulriple;
        //总共出现多少次
        int time = factor/tFactor;
        result = result + maxHigh * time * mulriple;
        mulriple *= 10;
    }
    //修正结果,如果最高位大于k,则增加当前位进制大小的个数,
    //如900之于6,存在一个600,则统计中增加了一个100,即factor
    if (maxHigh > k) {
        result += factor;
    }
    //计算剩余
    n = n - maxHigh * factor;
    if (maxHigh == k) {
        result = result +n +1;
    }
}

我们该如何计算剩余的部分计数呢?原理其实是一样的,我们只需要将这个数在进过一次过程就行了,因此我们可以写一个while循环,循环的结束条件是,只剩下个位。

int digitCounts(int k, int n) {
    int result = 0; //结果
    int base = 1;//最少是一位数
    int tmp = n; //n的副本
    int factor = 1;
    //计算一共有多少位
    while (tmp > 9){ 
        base++;
        tmp /= 10;
        factor = factor * 10;
    }
    while (base > 1){
        //计算最高位
        int maxHigh = n / factor;
        //从个位到十位,到百位...,累计出现的次数
        //个位上出现一次就计数加1,十位上出现一次计数加10...
        //计数倍数
        int mulriple = 1;
        for (int i = 1; i < base; i++) {
            //计算在个位,十位,百位...出现的次数
            //个位,每隔10个数,出现一次
            int tFactor = 10 * mulriple;
            //总共出现多少次
            int time = factor/tFactor;
            result = result + maxHigh * time * mulriple;
            mulriple *= 10;
        }
        //修正结果,如果最高位大于k,则增加当前位进制大小的个数,
        //如900之于6,存在一个600,则统计中增加了一个100,
        //即factor
        if (maxHigh > k) {
            result += factor;
        }
        //计算剩余
        n = n - maxHigh * factor;
        if (maxHigh == k) {
            result = result +n +1;
        }
        
        base--;
        factor /= 10;
    }
    //将只有一位的情况也加上
    if (n >= k) {
        result++;
    }
    return result;
}

这样就完成了所有代码,但是这还远远不够,为什么这么说,因为我们上面所有的情况都没有考虑0的情况,我们发现,对n =1000,k = 3, 有这样的数字满足情况,3,30-39,40-49..300-399,当k = 0时,我们并不能找到,00-09,,000-099,这样的数,但是我们去将这种情况考虑进去了,因此我们需要去掉。

int digitCounts(int k, int n) {
    int result = 0;
    int base = 1;//最少是一位数
    int tmp = n;
    int factor = 1;
    while (tmp > 9){//判断一共有多少位数,
        base++;
        tmp /= 10;
        factor = factor * 10;
    }
    int tmpBase = base;
        
    while (base > 1){
        int maxHigh = n / factor;
        //出现一次计数多少个
        int mulriple = 1;
        for (int i = 1; i < base; i++) {
            //出现的次数,比如说,个位,没10个出现1次,出现了10次
            //十位,每100出现了1次,
            int tFactor = 10 * mulriple;
            int time = factor / tFactor;
            //当0出现在最高位时,不做计数,在计算剩余的时候却可以计数。10010,此时0010中00是起作用的
            if (tmpBase == base && k == 0 && i > 1) {
                time = time - 1;
            }
            result = result + maxHigh * time * mulriple;

            mulriple *= 10;
        }
        if (maxHigh > k) {
            if (k > 0) {
                result += factor;
            }
            //计算剩余的时1100,此时计数100就需要将计数加上
            else if (k == 0 && base < tmpBase) {
                result += factor;
            }
        }
        n = n - maxHigh * factor;
        if (maxHigh == k) {
            result = result + n +1;
        }
        base--;
        factor /= 10;
    }
    if (n >= k) {
        result++;
    }
    return result;
}

你可能感兴趣的:(lintCode题解(3))