剑指Offer44 -- 思维
1. 题目描述
剑指 Offer 44. 数字序列中某一位的数字
2. 思路
完全借鉴的参考思路。补位思想。
首先,什么是(数字的)补位。
试想这么一种情况,所有数字的位数都相同,什么意思呢?例如:
1
,
222
1,222
1,222
他们的位数是不相同的,
1
1
1 是
1
1
1 位,
222
222
222 是三位,我们可以通过添加前置
0
0
0 来使得他们的位数相同,注意 ,前置
0
0
0 不一定只能在位数小的一方添加,例如:
001
001
001,
222
222
222
0001
0001
0001,
0222
0222
0222
…
都是可行的
好了,现在我们知道了什么是补位,那么,如果所有数字都是经过补位且位数相同,假设位数为
l
e
n
len
len,那么,想知道第
k
k
k 位所在的数字是什么,是非常非常简单的!
如果你不能立马想出来话,直接把这样
001
001
001,
002
002
002,
003
003
003,
004
004
004,…,
999
999
999 的数字看作是一个
1000
1000
1000 行,
3
3
3 列的矩阵!
每个数字都代表一行,每个数的位数就是列数。通过
k
/
1000
k/1000
k/1000,可以得到行,在
k
%
3
k\%3
k%3,即可得到列。
当然,这里我们假设,数字是连续的,不会出现,
1
1
1 完了就是
100
100
100 的情况,当然,题目条件就是这样的。
那么,现在我给你一个数字
k
k
k,请你告诉我从左到右,从上到下,第
k
k
k 个位置的数字是是什么(假设最左上角的下标为
0
0
0)?
这在八数码问题应该碰到过了吧,它的下标就是 [
k
/
1000
k/1000
k/1000,
k
%
3
k\%3
k%3]。
非常好,现在我们不仅知道什么是补位,而且还知道了在补位的情况下如何求得位置
k
k
k 所在位置(下标)处的数字是什么,剩下要做的,就是将原序列:
1
1
1,
2
2
2,…
100
100
100,… 补成一个位数相同的序列,当然,我们希望这个位数越少越好。
我们循环枚举位数
l
e
n
len
len 就可以了。
3. 代码
class Solution {
public:
// 我们把 k 的类型改为 long,是因为 k 可能会溢出,当 len=10 时,虽然此时会失败,但是会溢出
// 我们也可以将 len*pow(10, len) 改为 (long long)len*pow(10,len)
int findNthDigit(long k) {
for(int len = 1; len <= 10 ; len ++ ) { // 枚举所有的位数
if(k <= len * pow(10, len)) { // 长度为 len 的数字的位数之和
return to_string(k / len)[k % len] - '0';
}
k += pow(10, len); // 如果不够,补位,需要对前面的所有数字补位,
}
// 永远不会到这里
return -1;
}
};
4. 参考
神仙思路
评论区解释
最终步骤是获取到取某个数的第几位
如果所有数的位数相同都为i,在已知要取第k位的情况下,结果就是to_string(k / i)[k % i] - ‘0’
但是并不是所有数位数一样,比如说1、10、100分别是1、2、3位数,这时候我们就要想办法把它们变成位数相同的001、010、100。
由于前边较小的数数位增加了,相对的,要取的第k位也要增加。
由于我们数位i每增加一次就进行一次k的增加,所以可以视为只会有i-1和i位数,所以k每次增加的数=低于当前数位i的数字总个数pow(10, i) * 1。
这样,i * pow(10, i) > k成立时,表示所需数据就是i位,可以直接用to_string(k / i)[k % i] - '0’得出结果了