NowCoder老是力争上游,凡事都要拿第一,因此他对“1”这个数情有独钟。爱屋及乌,他也很喜欢包含1的数,例如十、十一、12……。你能帮他统计一下有多少个包含1的正整数吗?java
输入有多组数据,每组数据包含一个正整数n,(1≤n≤2147483647)。算法
对应每组输入,输出从1到n(包含1和n)之间包含数字1的正整数的个数。测试
1 9 10 20
1 1 2 11
假设有数字X(n)=xnxn−1…x2x1x0,xi上的权重是10i。
先考虑0~9n9n−1…929190,中出现1的数字个数,假设它是P(n),它由三部分组成:
- 0~9n−1…929190,含有1的数字数目是P(n−1)
- 1n0n−1…929190~1n9n−1…929190,含有1的数字数目是10n−1
- 1<ji<xi,ji0i−1…020100 ji9i−1…929190中含有1的数字数目是P(n−1),ji能够取8个数字。
因此P(n−1)=P(n−1)+10n−1+8∗P(n−1)=9∗P(n−1)+10n−1,又n=0时,P(n)=1,综上有:spa
P(n)={19∗P(n−1)+10n−1n=0n>0code
再考虑X(n),从右到左处理X(n)上的每一位,假设当前处理第i位。则要分三种状况:
第一种:xi=0,则xixi−1…x2x1x0与xi−1…x2x1x0含有1的数字个数相同,则F(i)=F(i−1)。
第二种:xi=1,则xixi−1…x2x1x0包含1的由两部分组成:
- 0~9i−1…929190中含有1的数字数,为P(i−1)
- xi0i−1…020100~xixi−1…x2x1x0中含有1的数字,为X(i−1)+1
则有F(i)=P(i−1)+X(i−1)+1
第三种:xi>1,则xixi−1…x2x1x0包含1的由四部分组成:
- 0~xi−1…x2x1x0中含有1的数字数,为P(i−1)
- 1i0i−1…020100~1i9i−1…929190中含有1的数字数,为10i−1
- 1<ji<xi,ji0i−1…020100~ji9i−1…929190中含有1的数字数,为P(i−1)。j_i能够取xi−2个
- xi0i−1…020100 xixi−1…x2x1x0中含有1的数字,为F(i−1)
则有F(i−1)=P(i−1)+10i−1+(xi−2)P(i−1)+F(i−1)=(xi−1)P(i−1)+10i−1+F(i−1)
综合有:
当n=0时,X(n)=x0图片
F(n)=1get
当n>0时,X(n)=xnxn−1…x2x1x0io
F(n)=⎧⎩⎨F(n−1)P(n−1)+X(n−1)+1(xn−1)P(n−1)+10n−1+F(n−1)xn=0xn=1xn>1for循环
import java.util.Scanner; /** * Declaration: All Rights Reserved !!! */ public class Main { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); // Scanner scanner = new Scanner(Main.class.getClassLoader().getResourceAsStream("data.txt")); while (scanner.hasNext()) { int n = scanner.nextInt(); System.out.println(countOne(n)); } scanner.close(); } /** * 【方法一】 * 计算[1-n]中包含数字1的数字个数 * * @param n 最在范围 * @return 包含数字1的数字个数 */ private static int countOne(int n) { int countedN = 0; int result = 0; // 从右向左分析n的每一位;for循环中:i 表示分析到了哪一位,i=1表示个位,i=10表示十位,以此类推; // onesPerI 表示从0到i-1含有1的数的个数,0,1,19 ...; // cur 是目前分析的那一位的数值; // 举个例子: f(m,n) 表示从m到n,含有1的数字的个数。 // f(1,500) = f(1, 99)+f(100, 199)+f(200, 299)+(300, 399)+f(400, 499) // f(1, 99) = f(200, 299) = f(300, 399) = f(400, 499) // f(100, 199) = 100 for (int i = 1, onesPerI = 0, cur; n != 0; onesPerI = onesPerI * 9 + i, i *= 10, n /= 10) { // 当前数位的数值 cur = n % 10; if (cur == 0) { continue; } else if (cur == 1) { // onesPerI表示[1, i-1]含有1的个数,countedN表示比 result = onesPerI + countedN + 1; } else { result += (cur - 1) * onesPerI + i; } // 表示比第i位以及比第i位低的各位的数值,好比abcdef,如今处理万位,那么countN就是bcdef countedN += cur * i; } return result; } }