题目描述
链接:Here。
假如有一组字符串符合如下规律:
S 1 = 1 S 2 = 12 S 3 = 123 S 4 = 1234 ⋯ S 9 = 123456789 S 10 = 12345678910 ⋯ S 18 = 123456789101112131415161718 \begin{array}{} S_{1} =1\\ S_{2} =12\\ S_{3} =123\\ S_{4} =1234\\ \cdots \\ S_{9} =123456789\\ S_{10} =12345678910\\ \cdots \\ S_{18} =123456789101112131415161718 \end{array} S1=1S2=12S3=123S4=1234⋯S9=123456789S10=12345678910⋯S18=123456789101112131415161718
(对于 S n S_n Sn来说,将从 1 1 1到 n n n的数字拼接到一起)
现在我们把所有的字符串拼接起来,组成一个无限长的字符串 S = 1121231234 ⋯ 12345678910111213 ⋯ S = 1121231234 \cdots 12345678910111213 \cdots S=1121231234⋯12345678910111213⋯你能找出该字符串的第 n n n位数字是多少吗?
输入
一个整数( 1 1 1 < 整数 < 1 0 15 10^{15} 1015),表示所求的位数是多少位
输出
一个整数,表示该位上的数字是多少
解析
其实就是要确定第N
位数字所在的项是哪一项。这里分两层。
一层是 L e n ( S n ) Len(S_n) Len(Sn)的长度,一层是 L e n ( ∑ i = 1 n L e n ( S i ) ) Len(\sum_{i=1}^{n} Len(S_i)) Len(∑i=1nLen(Si))的长度,分别抽象成 f ( x ) , g ( x ) f(x), g(x) f(x),g(x),观察数字长度的特点就可以知道, f ( x ) f(x) f(x)是个特殊的等差数列,而 g ( x ) g(x) g(x)是特殊的等差数列的前 x x x项和。
公差依次是:
1 ∼ 9 10 ∼ 99 100 ∼ 999 ⋯ 1 0 n ∼ 1 0 n + 1 − 1 1 2 3 ⋯ n + 1 \begin{matrix} 1\sim 9 & 10\sim 99 & 100\sim 999 & \cdots & 10^{n} \sim 10^{n+1} -1\\ 1 & 2 & 3 & \cdots & n+1 \end{matrix} 1∼9110∼992100∼9993⋯⋯10n∼10n+1−1n+1
依照这个就可以很容易算出 f ( x ) , g ( x ) f(x), g(x) f(x),g(x),然后利用输入的 n n n二分求上界(可以参考你真的理解二分的写法吗 - 二分写法详解)算出 g ( x ) < n g(x) < n g(x)<n的最大 x x x;
利用这个 x x x算出 r e s = n − g ( x ) res = n - g(x) res=n−g(x),然后利用输入的 r e s res res二分求上界算出 f ( y ) < r e s f(y) < res f(y)<res的最大 y y y;
利用这个 y y y算出 r e s s = r e s − f ( y ) ress = res - f(y) ress=res−f(y), r e s s ress ress表示整数 y + 1 y + 1 y+1的第 r e s s ress ress位数字,这就是答案了。
这样做应该是最快的解法了。
代码
#include <bits/stdc++.h>
typedef long long LL;
int main()
{
auto calFG = [] (LL n) -> std::pair<LL, LL>
{
LL start = 0, d = 1, nines = 9, sum = 0, res = 0;
for (; sum + nines < n; ++d, sum += nines, nines *= 10) {
res += nines * start + nines * (nines - 1) / 2 * d;
start += nines * d;
}
return { start + d * (n - sum), res + (n - sum + 1) * start + (n - sum + 1) * (n - sum) / 2 * d };
};
auto bFind = [] (LL n, const std::function<LL(int)> & func) -> int
{
int l = 0, r = 1000000;
while (l < r) {
int mid = l + (r - l + 1) / 2;
if (func(mid) < n) {
l = mid;
} else {
r = mid - 1;
}
}
return l;
};
for (LL n; std::cin >> n; ) {
int ii = bFind(n, std::bind([&] (int m) -> LL
{
return calFG(m).second;
}, std::placeholders::_1));
LL res = n - calFG(ii).second;
int i = bFind(res, std::bind([&] (int m) -> LL
{
return calFG(m).first;
}, std::placeholders::_1));
LL ress = res - calFG(i).first;
assert(ress);
std::cout << std::to_string(i + 1).at(ress - 1) << std::endl;
}
return 0;
}