整数中1出现的次数(从1到n整数中1出现的次数):最直观的想法是,遍历1到n,然后将每一个数转换为字符串,再遍历字符串求字符1出现的次数,依次累加即可得到结果。其中,之所以将数字转换为字符串,是为了利用字符串的长度函数来求长度,避免整数位数不确定的情况。
int NumberOf1Between1AndN_Solution(int n) { string str; int res=0; for(int i=1;i<=n;i++) { str=to_string(i); for(int j=0;j<str.size();j++) { if(str[j]=='1') res++; } } return res; }
优化:数位dp,即按位统计当前位为1的个数。使用一个变量base表示当前位是个位、十位、百位还是更高位,初始base等于1即表示个位,使用一个变量res表示1的个数。首先将数字n分为三个部分,分别是当前位cur,其计算方式为cur=n/base%10,高位a,其计算方式为a=n/base/10,低位b,其计算方式为b=n%base。其中计算当前位cur为1的情况下数字的个数一共分为三种情况:第一种是cur=0的情况,如果要使得当前位为1,那必须向前借位,则高位范围为0~(a-1),低位范围为0~(m个9),其中m对应base的位数,比如base等于1则表示个位,于是低位范围为0,个数为1,比如base等于10则表示十位,于是低位范围为0~9,个数为10,比如base等于100则表示百位,于是低位范围为0~99,个数为100,以此类推,于是当前位为1的数字个数总共为a*base个;第二种是cur=1的情况,即当前位已经为1,则分为两种情况,一是高位为a,则低位范围是0~b,一共有1*(b+1)个,二是高位范围为0~(a-1),低位范围为0~(m个9),这种情况同上,一共有a*base个,则总共为a*base+(b+1);第三种是cur=2~9的情况,则使得当前位为1,高位范围为0~a,低位范围为0~(m个9),总共为(a+1)*base个。循环条件是base小于等于n,具体代码以及注释如下。
int NumberOf1Between1AndN_Solution(int n) { int res=0; //表示1的个数 int base=1; //表示当前位 int cur; //表示当前位 int a; //表示高位 int b; //表示低位 //base不能超过n 等于是可以的 while(base<=n) { cur=n/base%10; a=n/base/10; b=n%base; //cur=0 借位使得当前为1 则高位范围为0~(a-1) 低位为0~9(m个9) m对应base的位数 总共为a*base if(cur==0) res+=a*base; //cur=1 当前位为1 则分为两部分 一种情况是高位范围为0~(a-1) 低位为0~9(m个9) m对应base的位数 总共为a*base 另一种情况是高位为a 低位为0~b 总共为b+1 else if(cur==1) res+=a*base+(b+1); //cur=2~9 使得当前为1 则高位范围为0~a 低位为0~9(m个9) m对应base的位数 总共为(a+1)*base else res+=(a+1)*base; //向前进一位 base*=10; } return res; }