1. 程式人生 > >LeetCode 233. 數字1的個數

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

-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,因為1

0、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)。

水平有限,歡迎批評。