題目描述:
對於某一個數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