, 주어진 속성 다소 간단한 문제를 0
및 n
사이가를
얼마나 많은 숫자를 해결하기 위해 종종 유용 ?
하나의 제한을 고정하면 문제를 상당히 쉽게 해결할 수 있습니다. 간단한 문제가 해결되면, 당신은 간단한 뺄셈과 원래의 문제에 대한 해결책을 얻을 수 있습니다 : 한계 countTo
에 포함되어있는 한계가 countBetween
에 포함되어야한다 여부에 따라
countBetween(a,b) = countTo(b) - countTo(a)
또는 countTo(b ± 1) - countTo(a ± 1)
.
제외 한계가 발생할 수있는 경우 (회문 들어, I는 추정되지 않음), countTo(n)
부정적인 n
(카운트 척도에 대하여 일체로 기능 간주 할 것)에 대한 <= 0
해야한다.
그래서 우리가 첫 번째 부분, 우리가 할 수 있도록 0, 회문 아닌 척하면 우리는 첫 번째 부분에 대한보다 균일 한 공식을 얻을
palindromes_below(n) = #{ k : 0 <= k < n, k is a palindrome }
확인할 수 있습니다.
1 부 : 숫자가 d
인 회문이 몇 개 있습니까?
첫 번째 숫자는 0
일 수 없습니다. 그렇지 않으면 제한이 없으므로 9 개의 가능한 선택 사항이 있습니다 (임의의 자료에 b
).
마지막 자릿수는 첫 번째 자릿수는 첫 번째 자릿수와 같습니다.
d >= 3
인 경우 두 번째 숫자는 첫 번째 숫자와 임의로 독립적으로 선택할 수 있습니다. 그것은 또한 끝에서 두 번째 숫자를 결정합니다.
d >= 5
인 경우 자유롭게 세 번째 숫자를 선택할 수도 있습니다.
순간의 생각 d = 2*k + 1
또는 d = 2*k + 2
대한 제한없이 선택 될 수 k
자리하고 비 제로로 제한 될 한 디지트 (제)가 있다는 것을 나타낸다. 그래서
9 * 10**k
d
-digit 다음 회문 (기본 b
에 대한 (b-1) * b**k
)이 있습니다.
멋지고 간단한 공식입니다.
n
경우 짝수 숫자 : 것과 기하학적 합계 수식을 사용하여, 우리는 쉽게 N (10) (즉, 최대 n
자리이다)보다 작은 회문 수를 얻을 수있다 n
홀수
n/2-1 n/2-1
2 * ∑ 9*10**k = 18 * ∑ 10**k = 18 * (10**(n/2) - 1)/(10 - 1) = 2 * (10**(n/2) - 1)
k=0 k=0
경우이며, 숫자는
2 * (10 ** ((N-1)/2) - 1) + 9 * 10 ** ((N-1)/2) = 11 * (10 ** ((n-1)/2) - 2
(일반베이스의 경우 resp. (b+1) * b**((n-1)/2) - 2
). 꽤 균일로 더 이상하지만 아직 충분히 간단한
:
def palindromes_up_to_n_digits(n):
if n < 1:
return 0
if n % 2 == 0:
return 2*10**(n//2) - 2
else:
return 11*10**(n//2) - 2
(우리가 아직 0을 계산에 포함되지 않습니다 기억).
나머지 부분은 다음과 같습니다.k
자리 n > 0
을 감안하면 회문 < n
미만 k
자리 중 하나
- 회문은, 거기에 그들 중
palindromes_up_to_n_digits(k-1)
, 또는 n
보다 작은 정확히 k
자리
- 회문.
그래서 후자를 계산합니다.
2 부 :
하자 m = (k-1)//2
및
d[1] d[2] ... d[m] d[m+1] ... d[k]
n
(전체 물건의 진수 표현은 다른 기지에 대한 동일한 원리로 작동하지만 명시 적으로 그것을 언급하지 않는다 다음)이므로
k
n = ∑ d[j]*10**(k-j)
j=1
각 1 <= c[1] < d[1]
에 대해 우리는 회문
p = c[1] c[2] ... c[m+1] {c[m+1]} c[m] ... c[2] c[1]
(심지어 k
번씩 홀수 k
대한 두번 나타나는 c[m+1]
디지트를) 얻었다 자유롭게 m
c[2], ..., c[m+1]
숫자를 선택할 수있다. 이제
c[1]*(10**(k-1) + 1) <= p < (c[1] + 1)*10**(k-1) <= d[1]*10**(k-1) <= n,
그래서 모든 (c[1]
! 주어진 선택을위한)이 10**m
회문 n
보다 작습니다.
따라서 첫 번째 자릿수가 n
의 첫 자릿수보다 작은 - 자리 장식 문자가 있습니다.
n
보다 작은 첫번째 자릿수 d[1]
인 k
- 자리 장식 문자를 고려해 보겠습니다.
k == 2
인 경우 d[1] < d[2]
인 경우 1이 있고 그렇지 않은 경우는 1입니다.
이
d[1]*(10**(k-1) + 1) + c[2]*(10**(k-2) + 10)
<= p < d[1]*(10**(k-1) + 1) + (c[2] + 1)*(10**(k-2) + 10)
<= d[1]*(10**(k-1) + 1) + d[2]*(10**(k-2) + 10) <= n
(10 10**(k-2) + 10
교체 k == 3
를 들어, k > 3
가정) : k >= 3
경우 각 0 <= c[2] < d[2]
, 우리는 자유롭게 우리는 p < n
를 참조 회문
p = d[1] c[2] c[3] ... c[m] c[m+1] {c[m+1]} c[m] ... c[3] c[2] d[1]
을 얻기 위해 m-1
자리 c[3] ... c[m+1]
를 선택할 수 있습니다.
따라서 d[2]*10**(m-1)
k
- 첫 번째 숫자는 d[1]
이고 두 번째 숫자는 d[2]
보다 작습니다.
계속는 1 <= r <= m
대해, 그 제 r
숫자이다 d[1] ... d[r]
및 그 r+1
세인트d[r+1]
자리보다 작은
d[m+1]*10**(m-r)
k
-digit 회문있다.
n
의 해당 자릿수 동일
n
의 해당 숫자보다 작은 제
m+1
숫자 중 하나가
(d[1]-1])*10**m + d[2]*10**(m-1) + ... + d[m]*10 + d[m+1]
k
-digit 회문 모든 선행 자리 있는데, 합산. 분명히 이들은 모두 n
보다 작습니다.
그 첫번째 m+1
자리 d[1] .. d[m+1]
입니다 하나 k
-digit 회문 p
있다, 우리는 너무 경우 p < n
을 계산해야합니다.
그래서, 마무리, 지금은 너무 0을 통합, 우리가 얻을
def palindromes_below(n):
if n < 1:
return 0
if n < 10:
return n # 0, 1, ..., n-1
# General case
dec = str(n)
digits = len(dec)
count = palindromes_up_to_n_digits(digits-1) + 1 # + 1 for 0
half_length = (digits-1) // 2
front_part = dec[0:half_length + 1]
count += int(front_part) - 10**half_length
i, j = half_length, half_length+1
if digits % 2 == 1:
i -= 1
while i >= 0 and dec[i] == dec[j]:
i -= 1
j += 1
if i >= 0 and dec[i] < dec[j]:
count += 1
return count
(영업 오해하지 않는 한), 우리는 다음
이 한계는 모두 주어진 문제에 대한 계산에 포함 할되기 때문에 빠른 솔루션
def count_palindromes(start, end):
return palindromes_below(end+1) - palindromes_below(start)
는 :
>>> bench(10**100,10**101-1)
900000000000000000000000000000000000000000000000000 palindromes between
10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
and
99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999
in 0.000186920166016 seconds
이 코드 잼에 현재 활성화 된 문제입니다. 자격 라운드는 12 시간 이내에 종료됩니다. – Ben
@Ben 정말요? 나는 그것을 표시했다 – jamylak
@ 벤 그것은 유감이다. 좋은 빠른 해결책에 문제가 있습니다. –