[Codewar练习-java]Block sequence(大数区块)

问题:

段位: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来计算得出:

\sum_{1}^{n}x=n*1+\frac{n(n-1)}{2}*1(1<=n<=9)

那么从 10 ~ 99 开始,每个 block 就会相差长度变为 2,10 ~ 99 长度等差就变成 等差 = 2 的公式,而其中计算得出:

  \sum_{10}^{n}x=n*11+\frac{n(n-1)}{2}*2(10<=n<=99),其中 n * 11 的 11 是 1~9 拼接 10 得出的 11 个字符

同理得出 100 ~ 999:

\sum_{100}^{n}x=n*192+\frac{n(n-1)}{2}*3(100<=n<=999), n * 192 = n * (9 + 180 + 3) 得出

当无限长的时候,继续用对等差公式通式进行编程。

2、当输入求 第 N 个字符,先进行 d 个通项等差求和公式运算,每次运算得出的字符数量和 N相减,直到 运算得出字符数量 > N,求出 目标等差通项

3、当求到相应运算字符也就知道了 第 d 个通项公式,也就是拼接到第 d 位数字就是我们的目标,同时 N 也计算得出拼接的长度,然后就需要对通项公式 得出 统计结果 反过来求出是哪一位数字是 目标数字

原来的等差通项:\sum_{n1}^{n2}x=S=n*a_{0}+\frac{n(n-1)}{2}*d(n1<=n<=n2)然后进行一元二次方程解得出,

n=\frac{\frac{d}{2}-a+\sqrt{(a-\frac{d}{2})^{2}+4*S*\frac{d}{2}}}{2\frac{d}{2}}(n1<=n<=n2) (自行查看一元二次方程解)

这个就是反过来求多少个数字相加的通项,求到目标数字

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;
        }
    }
}

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值