"How many numbers between 1 and 10^18 have no two equal adjacent digits?" You can't loop over 10^18 values, but you can walk the digits. That's digit DP.
Write a function count(N) = how many numbers in [0, N] satisfy the property. Then the answer for [L, R] is count(R) - count(L - 1).
from functools import lru_cache
def count(N):
if N < 0: return 0
digits = list(map(int, str(N)))
@lru_cache(None)
def go(pos, prev, tight, started):
if pos == len(digits):
return 1
limit = digits[pos] if tight else 9
total = 0
for d in range(limit + 1):
if started and d == prev:
continue
total += go(pos + 1,
d if (started or d) else -1,
tight and d == limit,
started or d != 0)
return total
return go(0, -1, True, False)
def in_range(L, R):
return count(R) - count(L - 1)
States are positions (about 19) × problem state × 2 × 2, each trying 10 digits. That's tiny compared to the range, typically well under a million operations.
Leading zeros silently break "adjacent digit" or "distinct digit" properties; that's what started is for. And memoising on tight=True states is fine within one N, but clear the cache between different N values.
Disclosure: written by the linkjob.ai team (real-time coding interview help). Free to reuse for practice.