0305 自反位置
* * * *
拉格朗日计划
* * * *
自反位置

将正整数从1开始依次拼接,得一无限长的字符串 $$S=12345678910111213141516171819202122232425\ldots$$ 容易看出,任何数在S中都会出现无限多次。令$f(n)$为n在S中第n次出现时的起始位置(从1开始)。例如,$f(1)=1$,$f(5)=81$,$f(12)=271$,$f(7780)=111111365$。

求$\sum_{k=1}^{13}f(3^k)$。

本题难度:



解答

$3^{13}=1594323$并不是太大的数字,因此本题的基本思想是考察从1到n的所有数字的拼接字符串中出现了几次n,然后在n前面添加数位来获得其第n次出现的位置。

这是因为数字n可以提前出现在两个数字的拼接中,例如$3^5=243$可以出现在数字42和43的拼接中。

此处的实现十分繁琐,以下代码改编自官网论坛,代码的逻辑和注释都比较清晰,不言自明。

最终结果是$18174995535140$。

注:以下为Python 3代码,代码中打印了进度信息。

r=0
for p in range(1,14):
    x=3**p
    sx=str(x)
    nums=set()
    d=len(sx)
    offset=0

    # 1. All x in y1y2 with y1 shorter than x (e.g. 243 in 42, 43)
    for i in range(1,d//2+1):
        y=int(sx[:i])
        sy=str(y+1)
        if sy==sx[d-len(sy):]:
            offset+=1

    # 2. All rotations of x
    for i in range(d):
        s=sx[i:]+sx[:i]
        if s[0]!='0':
            nums.add(int(s))

    # 3. All ___x___ and all x2___x1 ('x1x2' = 'x')
    k,p10=1,10
    while len(nums)+offset < x:
        offset+=len(nums)
        nums=set()
        for y in range(p10):
            sy=str(y).rjust(k,'0')
            for i in range(1,k+1):
                s=sy[:i]+sx+sy[i:]
                if s[0]!='0':
                    nums.add(int(s))
            for i in range(d):
                s=sx[i:]+sy+sx[:i]
                if s[0]!='0':
                    nums.add(int(s))
        k+=1
        p10*=10

    # 4. Sort nums and get n=x-th number
    nums=sorted(nums)
    n=nums[x-1-offset]

    # 5. Count digits before n
    s=str(n)
    d=len(s)
    k=p10=1
    pos=0
    while k < d:
        pos+=9*k*p10
        k+=1
        p10*=10
    pos+=k*(n-p10)

    # 6. Adjust for position in n, n+1
    s+=str(n+1)
    pos+=s.index(sx)+1

    print(p,pos)
    r+=pos

print(r)