求出不超过n的幸运数

题目描述:

对于某一个数m,如果它只包含数字4或者7(如44,47,77),我们称之为幸运数。现在输入一个自然数x,求出不超过x的幸运数的个数。
注意:结果可能非常大,请将答案除10^9+7取余数。

思路一:

1. 引入队列queue,开始时将4,7入队。
2. while(queue.peek() <= x)
   则将queue头部元素top出队,并且将top*10+4和top*10+7入队
   count++;
   这样形成数字序列:4,7,44,47,74,77,444,447...
3. 当2跳出循环时,return count;

算法有点思路简单,但是每出队一个数,将入队两个数,空间复杂度高。

思路二:
可以发现:

不考虑x数值大小,x位数与幸运数的个数关系如下:
位数   幸运数个数      可能值 
1       2           47
2       2+4=6       4744477477
3       2+4+8=14    4744477477444...,777
...
n       2+4+8+...+2^n = 2^(n+1)-2

尝试用动态规划求解。
求出不超过n的幸运数_第1张图片

1. 如果x有len位数,那么对于1~(m-1,m)~len而言,假定m-1处有pre个幸运数,m处有cur个幸运数,如上图所示。
2. 那么针对Xm数值,分类考虑:
if (Xm < 4)
    cur = 2^m - 2;
else if (Xm == 4)
    /**
    *比如:Xm=4,XmXm-1Xm-2...X1
    *(1)4Xm-1Xm-2...X1为幸运数的情况(与Xm-1Xm-2...X1为幸运数的情况相同);pre
    *(2)m-1位数中幸运数的个数:2^m - 2
    *(3)将(1)和(2)合并,需要注意的是,两者有一部分幸运数是相同的,因此需要减去相同幸运数的个数2^(m-1) - 2
    */
    cur = pre - [2^(m-1) - 2] + [2^m - 2]
    cur = pre + 2^(m-1);
else if (4 < Xm && Xm < 7)
    cur = pre + [2^(m+1) - 2] - 4
    /**
    *同 Xm == 4的情况类似。
    *比如:Xm=7,XmXm-1Xm-2...X1
    *(1)7Xm-1Xm-2...X1为幸运数的情况(与Xm-1Xm-2...X1为幸运数的情况相同);pre
    *(2)499...9(m-1个9)内幸运数的个数:2 *(2^m - 2)- [2^(m-1) - 2]
    *(3)将(1)和(2)合并,需要注意的是,两者有一部分幸运数是相同的,因此需要减去相同幸运数的个数2^(m-1) - 2
    */
else if (Xm == 7)
    cur = pre + 2 *(2^m - 2)- [2^(m-1) - 2] -[2^(m-1) - 2]
    cur = pre + 2^m
else
    cur = 3 * (2^m - 2)
从1~m循环上述过程,并更新pre
pre = cur

return pre

有了上述关系式,写出代码如下:

/**
 * Author:   Mengjun Li
 * Date:     2017/10/14 下午9:06
 * Description:只含4or7的数字称为幸运数,给定一个数n,求出不超过n的幸运数的个数
 */

/**
 * @author Mengjun Li
 * @create 2017/10/14
 * @since 1.0.0
 */

public class Main2 {

    public static void main(String[] args) {

        int x = 4;
        System.out.println("不超过" + x + "的幸运数的个数: " + cal(x));
        x = 77;
        System.out.println("不超过" + x + "的幸运数的个数: " + cal(x));
        x = 100;
        System.out.println("不超过" + x + "的幸运数的个数: " + cal(x));
        x = 444;
        System.out.println("不超过" + x + "的幸运数的个数: " + cal(x));
        x = 777;
        System.out.println("不超过" + x + "的幸运数的个数: " + cal(x));
        x = 888;
        System.out.println("不超过" + x + "的幸运数的个数: " + cal(x));
        x = 21857711;
        System.out.println("不超过" + x + "的幸运数的个数: " + cal(x));

    }

    public static int cal(int num) {
        //为了便于对num的每一个位进行操作,将其转化为String对象
        String str = String.valueOf(num);
        int len = str.length();
        int pre, cur = 0;
        if (str.charAt(len - 1) < '4')
            pre = 0;
        else if (str.charAt(len - 1) < '7')
            pre = 1;
        else
            pre = 2;
        int index = len - 2, tmp = 0;
        final int div = (int) (Math.pow(10,9)+7);
        while (index >= 0) {
            tmp = str.charAt(index) - '0';
            if (tmp < 4)
                cur = (1 << len - index) - 2;
            else if (tmp == 4)
                cur = pre + (1 << len - index - 1);
            else if (tmp < 7)
                cur = (1 << len - index + 1) - 4;
            else if (tmp == 7)
                cur = pre + (1 << len - index);
            else
                cur = (1 << len - index + 1) - 2;
            index--;
            pre = cur % div;
        }
        return pre;
    }
}

输出如下:

不超过4的幸运数的个数: 1
不超过77的幸运数的个数: 6
不超过100的幸运数的个数: 6
不超过444的幸运数的个数: 7
不超过777的幸运数的个数: 14
不超过1000的幸运数的个数: 14
不超过21857711的幸运数的个数: 254

你可能感兴趣的:(数据结构与算法)