题目描述:
对于某一个数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 4,7
2 2+4=6 4,7,44,47,74,77
3 2+4+8=14 4,7,44,47,74,77,444,...,777
...
n 2+4+8+...+2^n = 2^(n+1)-2
尝试用动态规划求解。
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