题目描述
求出113的整数中1出现的次数,并算出1001300的整数中1出现的次数?为此他特别数了一下1~13中包含1的数字有1、10、11、12、13因此共出现6次,但是对于后面问题他就没辙了。ACMer希望你们帮帮他,并把问题更加普遍化,可以很快的求出任意非负整数区间中1出现的次数(从1 到 n 中1出现的次数)。
C++跟java的不同在于,C++可以直接通过指针去操作内存里面的东西,但是Java需要通过借助数组去实现,对于数组的访问C++直接指针上面加减便可以,但是Java需要借助数组的下标。
复杂度比较高的办法
对输入的每个数字进行判断,然后累加结果。
public int NumberOf1Between1AndN_Solution(int num){
if(num <= 0) return 0;
int count = 0;
for(int i=1;i<=n;i++){
count += counNumOf1(i);
}
return count;
}
public int countNum(int n){
int result = 0;
while(n>0){
int mod = n%10;
if(mod == 1){
result++;
}
n = n/10;
}
return result;
}
时间复杂度为O(logn) 虽然书上这么说,但是由于Java里面没有C++的aoti的方法,所以我觉得复杂度是o(nlogn)
思路主要是:1 - 21345分解为1346 - 21345和1-1345 再把1346-11345,11346-21345这样做的目的是为了保证四位数里面可以从1000-9999都可以实现。
而终止条件为:当长度为1的时候判断最后最后一位,如果是0返回0,如果大于0返回1.
- 先判断最高位为1共有多少个。具体的判断的方法为如果最高位大于1的话,说明包含最高位为1的个数为Math.pow(10,length);length表示数字的总的长度,如果最高位为1的话,那么个数位,除去最高位以后的数字+1个
- 判断除去最高位以后还有多少个1,这个时候就可以数一下除去最高位的位数为n,第一位为first证明这里面有几个区间,然后后面的位数都可以从0-9随机选择,nfirstMath.pow(10,n-1)
- 然后去递归的找,除去最高位的数字以后剩余的内容。
具体的实现的代码:
public int NumberOf1Between1AndN_Solution(int num){
if(num <= 0) return 0;
char[] arr = String.valueOf(num).toCharArray();
return findNumberOf1(arr,0,arr.length);
}
public int findNumberOf1(char[] arr,int index,int length){
int first = arr[index] - '0';
if(length == 1 && first == 0){
return 0;
}
if(length == 1 && first > 0){
return 1;
}
int highNum = 0;
if(first >1){
highNum = (int)Math.pow(10,length-1);
}else if(first == 1){
highNum = countHighNumOf1(arr,index);
}
int exceptHighNumOf1 = first*(length-1)*(int)(Math.pow(10,length-2));
int leftNumOf1 = findNumberOf1(arr,index+1,length-1);
return highNum+exceptHighNumOf1+leftNumOf1;
}
public int countHighNumOf1(char[] arr,int index){
String s = "";
for(int i=index+1;i<arr.length;i++){
s += arr[i];
}
int result = Integer.valueOf(s);
return result + 1;
}