LeetCode 233. 數字1的個數
目標:
問題轉化:求最高位對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
你可能會問,這裡只考慮最高位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,因為1
然後剔除十位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)。
水平有限,歡迎批評。