The challenge
Consider the following array:
[1, 12, 123, 1234, 12345, 123456, 1234567, 12345678, 123456789, 12345678910, 1234567891011...]
If we join these blocks of numbers, we come up with an infinite sequence which starts with 112123123412345123456...
. The list is infinite.
You will be given an number (n
) and your task will be to return the element at that index in the sequence, where 1 ≤ n ≤ 10^18
. Assume the indexes start with 1
, not ``. For example:
solve(1) = 1, because the first character in the sequence is 1. There is no index 0.
solve(2) = 1, because the second character is also 1.
solve(3) = 2, because the third character is 2.
The solution in Java code
Option 1:
class Solution{
private static long bSearch(long n) {
long res = 0, i = 1, k = 9, j = 9;
for (; j < n; ++i, k *= 10, j += k){
res += i * k * (k + 1) / 2;
res += i * k * (n - j);
}
k = n - j / 10;
res += i * k * (k + 1) / 2;
return res;
}
public static int solve(long n) {
n--;
long lb = 0, rb = (long)1e9;
while (lb < rb){
long mb = (lb + rb + 1) / 2;
if (bSearch(mb) > n) rb = mb - 1;
else lb = mb;
}
n -= bSearch(lb);
long cnt = 9, len = 1;
while (n >= cnt * len){
n -= cnt * len;
cnt *= 10;
++len;
}
String x = String.valueOf(cnt / 9 + n / len);
int y = (int)(n % len);
return x.charAt(y) - '0';
}
}
Option 2:
import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
public class Solution {
public static int solve(long n) {
return getPoz(getShorterNumber(n));
}
private static long getShorterNumber(long n) {
long shortN = n;
long add = 1;
int digits = 1;
Numbers[] SAFE_TO_REMOVE = {
new Numbers(45L, 11L),
new Numbers(9045L, 192L),
new Numbers(1395495L, 2893L),
new Numbers(189414495L, 38894L),
new Numbers(23939649495L, 488895L),
new Numbers(2893942449495L, 5888896L),
new Numbers(339393974949495L, 68888897L),
new Numbers(38939394344949495L, 788888898L),
new Numbers(4393939398494949495L, 8888888899L)
};
for (int i = 0; i < SAFE_TO_REMOVE.length; i++) {
if (SAFE_TO_REMOVE[i].start >= n) {
if (i > 0) {
shortN = n - SAFE_TO_REMOVE[i - 1].start;
add = SAFE_TO_REMOVE[i - 1].add;
digits = i + 1;
}
break;
}
}
return getEvenShorterNumber(digits, add, shortN - 1);
}
public static long getEvenShorterNumber(long digit, long start, long n) {
BigDecimal b = BigDecimal.valueOf((start - 0.5 * digit));
double X = b.multiply(b)
.add(BigDecimal.valueOf(2).multiply(BigDecimal.valueOf(digit)).multiply(BigDecimal.valueOf(n)))
.sqrt(MathContext.DECIMAL64)
.subtract(b)
.divide(BigDecimal.valueOf(digit), 8, RoundingMode.HALF_UP)
.doubleValue();
long longX = (long) X;
long shorterNumber = (2L * start + (longX - 1L) * digit) * longX / 2L;
return (n - shorterNumber + 1);
}
private static int getPoz(long n) {
long n0 = n - 1;
long prevN;
int i = 0;
do {
prevN = n0;
n0 -= 9 * Math.pow(10.0, i) * (i + 1);
i++;
} while (n0 >= 0);
long number = (long) Math.pow(10.0, i - 1) + prevN / i;
int digitPoz = (int) (prevN % i);
String digit = String.valueOf(number).substring(digitPoz, digitPoz + 1);
return Integer.decode(digit);
}
private static class Numbers {
final long start;
final long add;
Numbers(long start, long add) {
this.start = start;
this.add = add;
}
}
}
Option 3:
class Solution{
public static long bSearch(long n){
long res = 0, i = 1, k = 9, j = 9;
for (; j < n; ++i, k *= 10, j += k){
res += i * k * (k + 1) / 2;
res += i * k * (n - j);
}
k = n - j / 10;
res += i * k * (k + 1) / 2;
return res;
}
public static int solve(long n){
n--;
long lb = 0, rb = (long)1e9;
while (lb < rb){
long mb = (lb + rb + 1) / 2;
if (bSearch(mb) > n) rb = mb - 1;
else lb = mb;
}
n -= bSearch(lb);
long cnt = 9, len = 1;
while (n >= cnt * len){
n -= cnt * len;
cnt *= 10;
++len;
}
String x = String.valueOf(cnt / 9 + n / len);
int y = (int)(n % len);
return x.charAt(y) - '0';
}
}
Test cases to validate our solution
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import org.junit.runners.JUnit4;
public class SolutionTest{
@Test
public void basicTests(){
assertEquals(1, Solution.solve(1L));
assertEquals(1, Solution.solve(2L));
assertEquals(2, Solution.solve(3L));
assertEquals(1, Solution.solve(100L));
assertEquals(2, Solution.solve(2100L));
assertEquals(2, Solution.solve(3100L));
assertEquals(1, Solution.solve(55L));
assertEquals(6, Solution.solve(123456L));
assertEquals(3, Solution.solve(123456789L));
assertEquals(4, Solution.solve(999999999999999999L));
assertEquals(1, Solution.solve(1000000000000000000L));
assertEquals(7, Solution.solve(999999999999999993L));
}
}