小米oj 找到第 N 个数字 II

题目

假如有一组字符串符合如下规律:
S 1 = 1 S 2 = 12 S 3 = 123 S 4 = 1234 … S 9 = 123456789 S 10 = 12345678910 S 11 = 1234567891011 ⋯ S 1 8 = 123456789101112131415161718 ⋯ S _1 =1 \newline S _2=12 \newline S _3=123 \newline S _4 =1234 \newline \dots \newline S _9 =123456789 \newline S_{10} =12345678910 \newline S _{11} =1234567891011 \newline ⋯ \newline S _18 =123456789101112131415161718 \newline ⋯ S1=1S2=12S3=123S4=1234S9=123456789S10=12345678910S11=1234567891011S18=123456789101112131415161718

(对于 S n S_n Sn来说,将从1到nn的数字拼接到一起)

现在我们把所有的字符串拼接起来,组成一个无限长的字符串 S = 1121231234 ⋯ 12345678910111213 ⋯ S = 1121231234 \cdots 12345678910111213\cdots S=112123123412345678910111213 你能找出该字符串的第nn位数字是多少吗?

输入

一个整数(1 < 整数 < 1 0 15 10^{15} 1015),表示所求的位数是多少位

输出

一个整数,表示该位上的数字是多少

输入样例

1
6
7
1234567
2345
234

输出样例

1
3
1
5
5
1

分析

  1. 首先分析每一行有多少个数字

分析表如下:

分段数字个数 (i表示行号)
S 1 − S 9 S_1 -S_9 S1S9i
S 10 − S 99 S_{10} -S_{99} S10S999+ (i -9)*2
S 100 − S 999 S_{100} -S_{999} S100S9999+ (99-9)*2+(i-99)*3

据此可推导出公式:
L e n ( S ( i ) ) = ∑ w = 1 w = l e n ( i ) − 1 w { ( 1 0 w − 1 ) − ( 1 0 w − 1 − 1 ) } + ∑ w = l e n ( i ) − 1 w = l e n ( i ) − 1 { i − ( 1 0 w − 1 ) } w Len(S(i)) = \sum_{w=1}^{w=len(i)-1} w\{(10^w-1) -(10^{w-1}-1)\} + \sum_{w=len(i)-1}^{w=len(i)-1} \{ i-(10^w -1) \} w Len(S(i))=w=1w=len(i)1w{(10w1)(10w11)}+w=len(i)1w=len(i)1{i(10w1)}w

可写作函数:


def lineLen(lineIndex):
    # w表示 位数
    w = len(str(lineIndex))

    result = 0
    for i in range(w):
        t1 = '9'* (i+1)
        t1 = int(t1)

        if i == 0:
            t2 = 0
        else:
            t2 = '9'*i
            t2 = int(t2)

        if i == w-1:
            result += (lineIndex-t2) * (i+1)
        else:
            result += (t1-t2) * (i+1)

    return result

现在我们可以使用函数lineLen(lineIndex)求出任意行的长度

  1. 计算前n 行有多少个数字

分析可知, 该数列可以拆分位几个等差数列

等差数列求和公式为:

L n = a 1 n + n ( n − 1 ) 2 d L_n = a_1 n + \frac{n(n-1)}{2} d Ln=a1n+2n(n1)d

分析表如下:

分段前i行的总数
L 1 − L 9 L_1 -L_9 L1L9 a 1 = 1 , n = i , d = 1 , L 1 ( i ) = a 1 n + n ( n − 1 ) 2 d a_1 = 1, n = i, d = 1, \newline L_1(i)= a_1 n + \frac{n(n-1)}{2} d a1=1,n=i,d=1,L1(i)=a1n+2n(n1)d
L 10 − L 99 L_{10} -L_{99} L10L99 a 1 = l i n e L e n ( 10 ) , n = i − 9 , d = 2 , L 2 ( i ) = L 1 ( 9 ) + a 1 n + n ( n − 1 ) 2 d a_1 = lineLen(10), n = i-9, d = 2, \newline L_2(i)= L_1(9)+a_1 n + \frac{n(n-1)}{2} d a1=lineLen(10),n=i9,d=2,L2(i)=L1(9)+a1n+2n(n1)d
L 100 − L 999 L_{100} -L_{999} L100L999 a 1 = l i n e L e n ( 100 ) , n = i − 99 , d = 3 , L 3 ( i ) = L 1 ( 9 ) + L 2 ( 99 ) + a 1 n + n ( n − 1 ) 2 d a_1 = lineLen(100), n = i-99, d = 3, \newline L_3(i)= L_1(9)+L_2(99)+a_1 n + \frac{n(n-1)}{2} d a1=lineLen(100),n=i99,d=3,L3(i)=L1(9)+L2(99)+a1n+2n(n1)d

代码如下:

# 计算前line行 有多少个数
def totalNum(line):
    w = len(str(line))
    seqstart = [0]*w
    seqstart[0] = 1
    for i in range(1,w):
        seqstart[i] = lineLen(10**i)
    
    resultsum = 0


    for i in range(w):
        if  i==w-1:
            n = line- (10**i -1)
        else :
            n = 9*(10**i)
        a1 = seqstart[i]
        d = i+1
        resultsum += n*a1 + (n*(n-1)/2) *d
    return int(resultsum)
  1. 查找, 需要查找:
    • 第n个数字在第几行
    • 找到行之后查找在改行的第几位

因为 S(i) 和L(i) 数列都是递增的,所以可以使用二分查找法.

代码如下:

# 使用二分查找,找到位置
def binaryFind(n,index,funcin):
    start = 1
    end = n+1
    while start <= end:
        mid = (start+end)//2
        if funcin(mid) >= index and funcin(mid-1) < index :
            return mid
        elif funcin(mid) < index:
            start = mid+1
        else :
            end = mid-1

    return "not find "

全部代码


import sys




def lineLen(lineIndex):
    # 公式: l(n) = 9 *1 + (99-9) *2 + ... +  (n- 9...9)*i

    # w表示 位数
    w = len(str(lineIndex))

    result = 0
    for i in range(w):
        t1 = '9'* (i+1)
        t1 = int(t1)

        if i == 0:
            t2 = 0
        else:
            t2 = '9'*i
            t2 = int(t2)

        if i == w-1:
            result += (lineIndex-t2) * (i+1)
        else:
            result += (t1-t2) * (i+1)

    return result


# 使用二分查找,找到位置
def binaryFind(n,index,funcin):
    start = 1
    end = n+1
    while start <= end:
        mid = (start+end)//2
        if funcin(mid) >= index and funcin(mid-1) < index :
            return mid
        elif funcin(mid) < index:
            start = mid+1
        else :
            end = mid-1

    return "not find "

# 计算前line行 有多少个数
def totalNum(line):
    w = len(str(line))
    seqstart = [0]*w
    seqstart[0] = 1
    for i in range(1,w):
        seqstart[i] = lineLen(10**i)
    
    resultsum = 0


    for i in range(w):
        if  i==w-1:
            n = line- (10**i -1)
        else :
            n = 9*(10**i)
        a1 = seqstart[i]
        d = i+1
        resultsum += n*a1 + (n*(n-1)/2) *d
    return int(resultsum)
    
def calculate(n):
    
    line = binaryFind(n,n,totalNum)
    
    index = n - totalNum(line-1)
    # 此时要找的数字是 s(line) 的第index 个数字
    if index < 10 :
        return index
    indexNum = binaryFind(line,index,lineLen)
    index = index - lineLen(indexNum-1)
    indexNum = str(indexNum)
    return indexNum[index-1]
    
    
if __name__ == "__main__":
    for line in sys.stdin:
        line = line.strip()
        if line == "":
            continue
        num = int(line)
        result = calculate(num)
        print(result)
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值