问题:
段位:4
说明:
比如说有这样一个规则数组 [1, 12, 123, 1234, 12345, 123456......] 里面每一个元素(也成为区块), 字符上都比前面一个多 i + 1 的数值,然后将整个数组拼接变成
1121231234123451234561234567....... 所有元素都拼接到一起,形成一个巨大的字符,那么当输入 1 的时候就返回这个巨大字符的 第一个字符 1,输入 i 就返回巨大字符的第 i 个字符。
题目连接:https://www.codewars.com/kata/5e1ab1b9fe268c0033680e5f/solutions/java
输入范围:
1 ≤ n ≤ 10^18
输入案例:
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.
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));
我的代码:
这个题目主要是大数,一个巨大的 long 运算,还有巨大的字符,首先直接一个一个枚举不行,拼接不行,只能进行逻辑运算:
1、(要查看等差公式的通式)
1 ~ 9 以内的巨大字符 block 1 12 123 1234 12345 ..... 每个 block 都相差长度 1,那么 1 ~ 9 以内的巨大字符长度可以用等差公式 等差 = 1来计算得出:
那么从 10 ~ 99 开始,每个 block 就会相差长度变为 2,10 ~ 99 长度等差就变成 等差 = 2 的公式,而其中计算得出:
,其中 n * 11 的 11 是 1~9 拼接 10 得出的 11 个字符
同理得出 100 ~ 999:
, n * 192 = n * (9 + 180 + 3) 得出
当无限长的时候,继续用对等差公式通式进行编程。
2、当输入求 第 N 个字符,先进行 d 个通项等差求和公式运算,每次运算得出的字符数量和 N相减,直到 运算得出字符数量 > N,求出 目标等差通项
3、当求到相应运算字符也就知道了 第 d 个通项公式,也就是拼接到第 d 位数字就是我们的目标,同时 N 也计算得出拼接的长度,然后就需要对通项公式 得出 统计结果 反过来求出是哪一位数字是 目标数字
原来的等差通项:然后进行一元二次方程解得出,
(自行查看一元二次方程解)
这个就是反过来求多少个数字相加的通项,求到目标数字
4、最后就是把剩下的 N 找到目标数字中的目标列就可以了
Java:
public class Blocksequence {
public static void main(String[] args) {
Solution s = new Solution();
int res = s.solve(1); // 1
// System.out.println(res);
// System.out.println();
// res = s.solve(2); // 1
// System.out.println(res);
// System.out.println();
// res = s.solve(3); // 2
// System.out.println(res);
// System.out.println();
// res = s.solve(100); // 1
// System.out.println(res);
// System.out.println();
// res = s.solve(2100); // 2
// System.out.println(res);
// System.out.println();
// res = s.solve(123456L); // 6
// System.out.println(res);
// System.out.println();
// res = s.solve(123456789L); // 3
// System.out.println(res);
// System.out.println();
// res = s.solve(999999999999999999L); // 4
// System.out.println(res);
// System.out.println();
// res = s.solve(1000000000000000000L); // 1
// System.out.println(res);
// System.out.println();
res = s.solve(317961737459799745L); // 6
System.out.println(res);
System.out.println();
}
static class Solution{
public static int solve(long n){
long begin = 0, number = 1, dd = 0;
long length = 0;
boolean falg = false;
// StringBuilder s = new StringBuilder();
// for(long i = 0, len = s.length(); i < n; i ++) {
// if(begin < len) {
// System.out.print(s.charAt(begin ++));
// }
// else {
// if(i != 0) System.out.println();
// begin = 0;
// s.append(number ++);
// System.out.print(s.charAt(begin ++));
// len = s.length();
// }
// }
// System.out.println();
// for(long i = 0; i < n;) {
// if(begin < length) {
// if(i + length < n) {
// i += length;
// begin = length;
// } else {
// if(!falg) {
// falg = true;
// dd = n - i;
// }
// i ++;
// }
// }
// else {
// length += String.valueOf(number ++).length();
// }
// }
// System.out.println(dd);
// System.out.println(number);
long d = 1, pre = 0, preMax = 0;
List<Long> list = new ArrayList<>();
list.add(0L);
while(0 < n) {
long num = ((long)Math.pow(10, d) - 1 - pre), a0 = preMax + d;
long areaMax = num * a0 + (num - 1) * num / 2 * d;
pre = (long)Math.pow(10, d) - 1;
preMax += num * d;
list.add(preMax); // 前缀集合
if(areaMax < n) {
n -= areaMax;
d ++;
} else {
// 先进行 Block 统计
BigDecimal powVal = new BigDecimal(2 * a0 - d).multiply(new BigDecimal(2 * a0 - d));
BigDecimal sqrtVal = powVal.add(
new BigDecimal(8)
.multiply(new BigDecimal(n))
.multiply(new BigDecimal(d)));
long sqrtLong = sqrt(sqrtVal, 4).setScale(0, BigDecimal.ROUND_DOWN).longValue(); // 防止四舍五入
num = (d - 2 * a0 + sqrtLong) / (2 * d);
n -= new BigDecimal(num).multiply(new BigDecimal(a0)).add(new BigDecimal(num - 1).multiply(new BigDecimal(num)).divide(new BigDecimal(2)).multiply(new BigDecimal(d))).longValue();
if(n == 0) { // 已经确认是 block 最后一个
d --;
int tens = d == 0 ? 0 : (int)Math.pow(10, d) - 1;
String siteStr = String.valueOf(tens + num);
return siteStr.charAt(siteStr.length() - 1) - '0';
} else { // 进入确认 block 第几位
for(int i = 1, size = list.size(); i < size; i ++) {
Long ll = list.get(i) - list.get(i - 1); // 需要减去差分
if(n > ll) n-= ll;
else { // 确认了有 i 位
long ni = n / i; // 先算出 i 位数值
long tens = (long)Math.pow(10, i - 1); // 将 i 位前数值算出
if(n % i == 0) {
String siteStr = String.valueOf(tens - 1 + ni);
return siteStr.charAt(siteStr.length() - 1) - '0';
} else {
String siteStr = String.valueOf(tens - 1 + ni + 1); // 因为 n 个字符有余, 所以是 ni + 1, 特意写出来
return siteStr.charAt((int)(n % i) - 1) - '0';
}
}
}
}
}
}
return -1;
}
public static BigDecimal sqrt(BigDecimal value, int scale){
BigDecimal num2 = BigDecimal.valueOf(2);
int precision = 100;
MathContext mc = new MathContext(precision, RoundingMode.HALF_UP);
BigDecimal deviation = value;
int cnt = 0;
while (cnt < precision) {
deviation = (deviation.add(value.divide(deviation, mc))).divide(num2, mc);
cnt++;
}
deviation = deviation.setScale(scale, BigDecimal.ROUND_HALF_UP);
return deviation;
}
}
}