按这样的方式将字符串分组:1,12,123,...,123...n,...
用 f【】表示每一组的长度(数字的个数),所以:
1 ~ 9 : f【i】= f 【i - 1】+ 1;
10 ~ 99 : f【i】= f 【i - 1】+ 2;
100 ~
999 : f【i】= f 【i - 1】+ 3;
1000 ~ 9999 : f【i】= f 【i - 1】+ 4;
10000 ~ 99999 : f【i】= f 【i - 1】+ 5;
同时用sum【】记录前i组总共的数字个数,递推关系为sum【i】= sum【i-1】+ f【i】
#include <iostream>
#include <cmath>
using namespace std;
#define MAXN 31268
unsigned int f[MAXN+1], sum[MAXN+1];
void init()
{
int i;
sum[0] = f[0] = 0;
for (i = 1; i <= 9; i++) { f[i] = f[i-1] + 1; sum[i] = sum[i-1] + f[i]; }
for (i = 10; i <= 99; i++) { f[i] = f[i-1] + 2; sum[i] = sum[i-1] + f[i]; }
for (i = 100; i <= 999; i++) { f[i] = f[i-1] + 3; sum[i] = sum[i-1] + f[i]; }
for (i = 1000; i <= 9999; i++) { f[i] = f[i-1] + 4; sum[i] = sum[i-1] + f[i]; }
for (i = 10000; i <= MAXN; i++) { f[i] = f[i-1] + 5; sum[i] = sum[i-1] + f[i]; }
/*
以上5个for循环可由以下代码代替,写成上面的形式更便于理解。
for (i = 1; i <= MAXN; i++) {
f[i] = f[i-1] + (int)log10(i+0.0) + 1;
sum[i] = sum[i-1] + f[i];
}
*/
}
int BSearch(int l, int r, int tar)
{
int m;
while (l < r) {
m = (l + r) >> 1;
if (sum[m] < tar) l = m + 1;
else r = m;
}
return l;
}
int getDigit(int m, int nth) {
int pos = nth - sum[m-1], len = 0, i;
//pos为所求数在第m组中的位置
//从1开始用len记录长度,当长度大于pos后跳出,注意此处其实加到i-1的时候pos>len就不成立了
for (i = 1; pos > len; i++)
len += (int)log10(i+0.0) + 1;
// 所求数实际是整数i-1的第len-pos+1位,return后的操作就是取出这一位数
return (i-1)/(int)pow(10.0, len-pos+0.0) % 10;
}
int main()
{
int t, n;
init();
cin >> t;
while (t--) {
cin >> n;
int m = BSearch(1, MAXN, n); //位置n属于第m个分组
cout << getDigit(m, n) << endl;
}
return 0;
}