题意:设字符串s1="1", s2="12", s3="123", ..., s13="12345678910111213", ...
求出串S = s1s2s3...s(k)...中的第x个数字(原题以i表示)。
最朴素的想法,直接生成S,最后直接取下标x的数字。这样做时间和空间都不允许(串长度可达2147483647)。
其实通过一些数学计算就可以做到:先算出第x个数字在哪个串s(k)中,并求出它在s(k)中的位置y。然后再求出在s(k) =
1 2 3 4 5 6 ... k中,位置y对应的数m,这个m可能是多位数,所以还要求出在m中的位置z。最后位置z的数字即答案。
所以第一步求k和y,第二步求m和z,第三步得答案。
要求k,首先得知道各个串s(i)的长度len[i],怎么求?
把串s(i)写成以下形式,会更清晰:
1
2
3
:
10
11
12
:
100
101
102
:
:
[i]
长度就是个位数、十位数……的个数总和,用几个判断条件,很容易算出。
然后求串s(k)中的新下标y,用辅助数组start[i]记录s(i)起始点所在的位置,那么y = x - (start[k] - 1)
现在问题转变成求1 2 3 ... k 中的第y个数字,还是将其写成如下形式:
1
2
3
:
10
11
12
:
100
101 = m
102
:
:
[k]
我们先要找到第y个数字所对应的数m,
m满足:len[m-1] < y <= len[m]
最后求出在m中是第几个数字(z):
z = y - len[m-1]
比如z=2,则最终答案就是0.
题目给出x <= 2147483647,可以事先算好最大可能的k值(MAXK),然后对len、start打表。
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#define MAXK 31268
using namespace std;
int len[MAXK + 1];
int start[MAXK + 1];
void make_len()
{
for (int k = 1; k <= MAXK; k++)
{
len[k] = 0;
if (k >= 1)
len[k] += k;
if (k >= 10)
len[k] += k - 9;
if (k >= 100)
len[k] += k - 99;
if (k >= 1000)
len[k] += k - 999;
if (k >= 10000)
len[k] += k - 9999;
}
}
void make_start()
{
start[1] = 1;
for (int k = 2; k <= MAXK; k++)
start[k] = start[k - 1] + len[k - 1];
}
int main()
{
int T;
cin >> T;
make_len();
make_start();
while (T--)
{
int x, y, z, k, m;
cin >> x;
/* find k in S1S2S3... and y in S(k) */
for (k = 1; k < MAXK; k++)
if (x < start[k + 1]) break;
y = x - (start[k] - 1);
/* find m in 12345...k and z in m*/
for (m = 1; m <= k; m++)
if (y <= len[m]) break;
z = y - len[m - 1];
/* answer: the z-th number in m */
char s[10];
sprintf(s, "%d", m);
cout << s[z - 1] << endl;
}
return 0;
}