题意
数字以 0123456789101112131415… 的格式序列化到一个字符序列中。在这个序列中,第5位(从下标0开始计数)是5,第13位是1,第19位是4,等等。
请写一个函数,求任意第n位对应的数字。
即在 给定的字符序列(01234567891011...)中返回第n位的数字(下标从0开始)
解题思路
- 将 101112⋯ 中的每一位称为 数位 ,记为 n ;
- 将 10, 11, 12,⋯ 称为 数字 ,记为 num;
- 数字 10是一个两位数,称此数字的 位数 为 2 ,记为 digit ;
- 每 digit 位数的起始数字(即:1, 10, 100, ⋯),记为 start。
观察上表,可推出各 digit 下的数位数量 count 的计算公式:count=9×start×digit。
根据以上分析,可将求解分为三步:
- 确定 n 所在 数字 的 位数 ,记为 digit;
- 确定 n 所在的 数字 ,记为 num ;
- 确定 n 是 num 中的哪一数位,并返回结果。
1. 确定所求数位的所在数字的位数
如下图所示,循环执行 n 减去 一位数、两位数、... 的 数位数量 count,直至 n≤count 时跳出。
由于 n 已经减去了一位数、两位数、...、(digit-1位数的 数位数量count ,因而此时的 n 是从起始数字 start 开始计数的。
digit, start, count = 1, 1, 9
while n > count:
n -= count
start *= 10 # 1, 10, 100, ...
digit += 1 # 1, 2, 3, ...
count = 9 * start * digit # 9, 180, 2700, ...
2. 确定所求数位所在的数字
此时,n的范围已经在 所在数字的位数 的数位数量 的范围之中了。
如下图所示,所求数位 在从数字 start 开始的 第 [(n - 1) / digit] 个 数字 中( startstart 为第 0 个数字)。
num = start + (n - 1) // digit
例如:n=10,由于在第一步确立 所求数位的所在数的位数 的时候,n已经减去了9,所以在到达第二步的时候,n已经变成1了,n的范围 从1~180,不会超过它所在位数的 数位数量的范围。
由于(n-1)/2=0,所以所求数位在 从数字start开始的第0位中,即在数字10中。
3. 确定所求数位在 num的哪一数位
如下图所示,所求数位为数字 num 的第 (n - 1) % digit 位( 数字的首个数位为第 0 位)。
s = str(num) # 转化为 string
res = int(s[(n - 1) % digit]) # 获得 num 的 第 (n - 1) % digit 个数位,并转化为 int
结论:
所求数位是 res 。
C++实现
class Solution {
public:
int findNthDigit(int n)
{
int digit=1; // n所在数字的 位数。初始化为1
long start=1; //每digit位数的起始数字(即1、10、100...),初始化为 位数为1的数字 的起始数字。
long count=9; //每digit位数的 数位数量。初始化为 位数为1的数字 的数位数量
//1、确定n所在数字的 位数。
while(n>count)
{
n=n-count;
start=start*10; //下一位数的 起始数字。
digit=digit+1; //下一个位数
count=9*start*digit; //下一个位数的 数位数量
}
//2、确定所求数位 所在的数字。
long num = start+(n-1)/digit;
//3、确定所求数位在num的哪一位数
string str_num = to_string(num);
int res = str_num[(n-1)%digit]-'0';
return res;
}
};
u1s1,这种数学推导的题遇上了,如果事先没有做过,大概率凉凉了。。。。太难了。。。