题目链接 https://leetcode-cn.com/problems/number-of-digit-one/description/
目标:
问题转化:求最高位对1的贡献
例如输入193,我们先求1作为最高位百位对1的贡献,假设结果为c1;然后我们将最高位1去掉,就剩下93,我们求得9作为最高位十位对1的贡献为c2;同样我们去掉9,剩下3,我们求得3作为最高位个位对1的贡献是c3。
答案 = c1+c2+c3。
那么,求最高位对1的贡献怎么求?按照以下规则:
如果n是一位数,最高位就是个位,由于没有比它更小的位数了,因此对1的贡献只有它本身(个位),如果各位数字大于等于1,贡献是1,否则贡献是0。
如果n是两位数,最高位就是十位,因此对1的贡献分为0-9部分和十位本身。0-9:贡献能力是1,再乘以十位数字就是贡献1的个数;十位:贡献大小取决于十位数字,如果十位数字大于1,则贡献是10;如果十位数字等于1,则贡献是去掉十位剩下的数+1。
如果n是三位数,最高位就是百位,因此对1的贡献分为0-99部分和它本身(百位)。0-99:贡献能力是20,百位:贡献大小取决于百位数字,如果百位数字大于1,则贡献是100;如果十位数字等于1,则贡献是去掉百位剩下的数+1。
如果n是四位数,最高位就是千位,因此对1的贡献分为0-999部分和它本身(千位)。0-999:贡献能力是300,千位:贡献大小取决于千位数字,如果千位数字大于1,则贡献是1000;如果千位数字等于1,则贡献是去掉千位剩下的数+1。
以此类推。。。。。。
举例说明:
413是个三位数,我们这里先考虑最高位4对1的贡献,根据上面的规则,贡献分为0-99部分和百位部分。首先说0-99贡献:0-99共有20个1,因此贡献能力是20,一共有四次(0-99、100-199、200-299、300-399),因此共贡献了4*20 = 80个1;再说百位的贡献:因为百位数字是4,大于1,因此百位的贡献就是100(100、101、...、199一共100个 1)。因此,413最高位4对1的贡献 = 4*20+100 = 180。
你可能会问,这里只考虑最高位4对1的贡献,后面的13也贡献了6个1。没错,因此我们考虑完4对1的贡献后,就要将4剔除,剩下13,我们还是套用此规则。
13是个两位数,我们这里只找最高位1对1的贡献,根据上面的规则,贡献分为0-9部分和十位部分。首先说0-9贡献:0-9共有1个1,因此贡献能力是1,一共有1次(0-9),因此共贡献了1*1 = 1个1;再说十位的贡献:因为十位数字等于1,因此十位的贡献就是4(去掉十位后的数+1,即3+1,因为10、11、12、13)。因此,13最高位十位1对1的贡献 = 1*1+4 = 5。
然后剔除十位1,只剩下3了,3大于等于1,所以贡献是1。
因此结果 = 4*20+100 + 1*1+4 + 1 = 186。
0-9贡献能力是1,0-99贡献能力是20,0-999贡献能力是300,0-9999贡献能力是4000,这是怎么来的?
1*10+10 = 20;20*10+10^2=300;300*10+10^3=400。就是这个规律。
最后上代码:
class Solution {
int GetContribution1(int nDigit){
if (nDigit == 1)
{
return 0;
}
int nTem = nDigit - 2;
int nRes = 1;
int i = 1;
while(nTem-- > 0)
{
nRes = nRes * 10 + power10(i++);
}
return nRes;
}
// 10的n次幂
int power10(int n)
{
int nTem = n;
int res = 1;
while (nTem > 0)
{
res *= 10;
nTem--;
}
return res;
}
public:
int countDigitOne(int n) {
int nRes = 0;
// 定义一个数组记录输入的每一位
vector<int> vecNum;
int nTem = n;
while(nTem)
{
vecNum.push_back(nTem % 10);
nTem /= 10;
}
nTem = n;
while(vecNum.size() > 0)
{
// 当前共有几位
int nCurWei = vecNum.size();
// 当前最高位是多少
int nHigh = vecNum.back();
// 当前最高位如果是0,则对1没有贡献
if (nHigh > 0)
{
// 贡献的第一部分
nRes += GetContribution1(nCurWei) * nHigh;
// 贡献的第二部分
if (nHigh == 1)
{
nRes += nTem % power10(nCurWei - 1) + 1;
}
else
{
nRes += power10(nCurWei - 1);
}
// nTem表示去除最高位剩下的数
nTem %= power10(nCurWei - 1);
}
vecNum.pop_back();
}
return nRes;
}
};
时间复杂度应该是O(log N)。
水平有限,欢迎批评。