按这样的方式将字符串分组: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; }