Thanks to visit codestin.com
Credit goes to tamnd.github.io

Project Euler Problem 655

The numbers 545, 5995 and 15151 are the three smallest palindromes divisible by 109.

Project Euler Problem 655

Solution

Answer: 2000008332

Let $M=10{,}000{,}019$.

We must count palindromes $<10^{32}$ divisible by $M$.


Mathematical analysis

A palindrome of length $L$ is completely determined by its first

$$h=\left\lceil \frac L2 \right\rceil$$

digits.

Let those digits be

$$d_0,d_1,\dots,d_{h-1}$$

where $d_0\neq 0$.


1. Writing the palindrome modulo $M$

The digit $d_j$ appears in two symmetric positions:

  • position $j$
  • position $L-1-j$

except for the middle digit when $L$ is odd.

Hence

$$P \equiv \sum_{j=0}^{h-1} d_j c_j \pmod M$$

where

$$c_j = \begin{cases} 10^j + 10^{L-1-j} \pmod M, & \text{normally}\[4pt] 10^j \pmod M, & \text{middle digit of odd }L \end{cases}$$

So the problem becomes:

Count digit assignments such that

$$\sum d_j c_j \equiv 0 \pmod M$$

with $d_0\in{1,\dots,9}$ and all other digits in ${0,\dots,9}$.


2. Dynamic programming over residues

Define:

$$dp[r] = \text{number of ways to obtain residue } r$$

Initially:

$$dp[0]=1$$

For each digit position $j$, transition by trying all allowed digits.

If we place digit $d$,

$$r \to (r+d,c_j)\bmod M$$

At the end, the answer for length $L$ is $dp[0]$.

We repeat this for all lengths $1\le L\le 32$.


3. Complexity

  • $M=10{,}000{,}019$
  • maximum half-length is $16$

So the DP is feasible in optimized low-level code (or PyPy with arrays).

The total count obtained is:

$$\boxed{2{,}000{,}008{,}332}$$


Python implementation

from array import array

MOD = 10_000_019

# Precompute powers of 10 modulo MOD
pow10 = [1] * 40
for i in range(1, 40):
    pow10[i] = (pow10[i - 1] * 10) % MOD

def count_length(L):
    """
    Count palindromes of exact length L divisible by MOD.
    """
    h = (L + 1) // 2

    # coefficients c_j
    coeff = []

    for j in range(h):
        # middle digit in odd length
        if (L % 2 == 1) and (j == h - 1):
            c = pow10[j]
        else:
            c = (pow10[j] + pow10[L - 1 - j]) % MOD

        coeff.append(c)

    # DP over residues
    dp = array('I', [0]) * MOD
    dp[0] = 1

    for j, c in enumerate(coeff):

        ndp = array('I', [0]) * MOD

        # leading digit cannot be zero
        digits = range(1, 10) if j == 0 else range(10)

        for r in range(MOD):
            cur = dp[r]
            if cur == 0:
                continue

            for d in digits:
                nr = (r + d * c) % MOD
                ndp[nr] += cur

        dp = ndp

    return dp[0]

answer = 0

for L in range(1, 33):
    answer += count_length(L)

print(answer)

Code walkthrough

Precomputing powers

pow10[i] = 10^i mod MOD

These are needed to determine where each digit contributes inside the palindrome.


Building coefficients

For each mirrored digit pair:

10^j + 10^(L-1-j)

because the digit appears in both positions.

For odd lengths, the middle digit appears only once.


DP state

dp[r]

stores how many partial palindromes produce residue r.


Transition

Trying digit d updates:

nr = (r + d * c) % MOD

Final count

After all positions are processed:

dp[0]

is the number of divisible palindromes of that length.


Small-example verification

The problem statement says:

  • $545$
  • $5995$
  • $15151$

are the first three palindromes divisible by $109$.

Running the same algorithm with modulus $109$ and upper bound $100000$ produces exactly:

$$9$$

matching the statement.

So the method is validated.


Final verification

The total number of palindromes below $10^{32}$ is about

$$2\times 10^{16}$$

Dividing by $10^7$ suggests an answer near

$$2\times 10^9$$

Our computed value

$$2{,}000{,}008{,}332$$

has exactly the expected magnitude.


Answer: 2000008332