Count Evenly Good Integers solution and explanation
The problem
You are given two integers l and r.
An integer is called evenly good if it contains an even number of even digits.
Return the number of evenly good integers in the inclusive range [l, r].
Example 1:
Input: l = 18, r = 22
Output: 3
Explanation:
The evenly good integers in the range [18, 22] are:
- 19, because it contains 0 even digits.
- 20, because it contains 2 even digits.
- 22, because it contains 2 even digits.
Thus, the answer is 3.
Example 2:
Input: l = 98, r = 101
Output: 2
Explanation:
The evenly good integers in the range [98, 101] are:
- 99, because it contains 0 even digits.
- 100, because it contains 2 even digits.
Thus, the answer is 2.
Example 3:
Input: l = 1, r = 10
Output: 5
Explanation:
The evenly good integers in the range [1, 10] are 1, 3, 5, 7, and 9, because each of them contains 0 even digits. Thus, the answer is 5.
Constraints:
1 <= l <= r <= 1015
Approach overview
Problem Overview: You are given a range of integers and must count how many of them satisfy the evenly good condition defined in the statement. The condition depends on the digits of each number, so you need a way to reason about digits without checking every value one by one when the range is large.
Approach 1: Brute Force Enumeration (O(N * d) time, O(1) space)
Iterate over every integer in the range, extract its digits with repeated division by 10, and test the condition on each one. Each check costs O(d) where d is the number of digits. This is correct and easy to verify, but when the bounds reach 10^18 it cannot finish. Use it to validate your reasoning on small inputs, not as the final answer.
Approach 2: Digit DP with Memoization (O(d * S * 10) time, O(d * S) space)
Process the number digit by digit from the most significant position. Track three things in the state: the current position, a tight flag that says whether the prefix so far equals the upper bound's prefix, and whatever digit-level summary the condition needs (for example, a running count, parity, or a set-like mask). At each position, try every digit from 0 to 9 that respects the tight limit, update the summary, and recurse. Memoize on (position, summary, tight) so each state is computed once. Counting the complete numbers in [0, N] and subtracting the count for [0, L - 1] gives the answer for any range. Prefer this approach whenever the bounds are large and the condition can be summarized in a small state.
Approach 3: Iterative Bottom-Up Digit DP (O(d * S * 10) time, O(S) space per layer)
Convert the recursive memoization into a table filled from the last digit position back to the first. This removes recursion depth concerns and lets you roll the table into two arrays, cutting memory from O(d * S) to O(S). The transitions are identical to Approach 2. Use this version in languages where recursion overhead or stack limits matter, such as C++ or Go under tight constraints.
Recommended for interviews: Start by stating the brute force and its complexity, then explain why it fails on large bounds. Interviewers expect you to reach digit DP and to explain the state definition clearly. The brute force shows you understand the problem. The digit DP with a correctly chosen state shows the skill they are looking for. Practice the broader pattern in dynamic programming and math problems, since digit DP reuses both ideas.
Solution
Thinking
rcan be as large as10^{15}, so checking every integer in[l, r]does not finish in time. The range count equalsF(r)-F(l-1), so it is enough to count integers up to one upper bound.An integer is evenly good exactly when its number of even digits is even, so the digits can be filled from high to low.
When
xhasddigits, every shorter integer shows up with leading zeros, and those zeros are even digits. On[0, 10^{d-1}-1]the count that includes those zeros equals the count from the ordinary decimal form, and a fulld-digit integer has no leading zero, so the two counts agree on[0, x].The search state is then the position, the even-digit count modulo
2, and whether the prefix is tight. Each digit is chosen from0through the current limit, an even digit flips the parity, and a finished number is counted when the parity is even.
Let F(x) be the number of evenly good integers in [0, x]. The answer is F(r)-F(l-1). The decimal form of 0 is the single digit 0, which has an odd number of even digits, so F(0)=0.
Write x as a decimal string s and search from high digits to low digits with memoization. dfs(pos, st, lim) is the number of ways to fill position pos when the even digits placed so far number st modulo 2, and lim says whether the prefix is tight against x.
At the end of the string, return 1 when st=0 and return 0 otherwise. The upper digit up equals s[pos] when the prefix is tight, and equals 9 otherwise. For a digit i from 0 to up, the next parity is (st+1)bmod 2 when i is even and stays st when i is odd. The next position stays tight only when lim is true and i=up.
Among 0 through 9, five digits are even and five are odd, so a free run of digits splits evenly between the two parities. When x has d\ge 2 digits, every integer with fewer digits appears in this search with leading zeros, and those integers are exactly [0, 10^{d-1}-1]. After padding to d digits the leading digit is the even digit 0 and the rest are free, which gives 10^{d-1}/2 strings with an even number of even digits. In the ordinary decimal form there are 5 one-digit evenly good integers, and a length k\ge 2 contributes exactly half of its integers. Summing lengths 1 through d-1 gives
$
5+sum_{k=2}^{d-1}\frac{9}{2}times 10^{k-1}=\frac{10^{d-1}}{2}.
An integer that already has d digits and does not exceed x has no leading zero, so the two writings match. The search therefore returns F(x).
The time complexity is O(log r) and the space complexity is O(log r)$.
class Solution:
def countEvenlyGoodIntegers(self, l: int, r: int) -> int:
@cache
def dfs(pos: int, st: int, lim: bool) -> int:
if pos >= len(s):
return st ^ 1
up = int(s[pos]) if lim else 9
return sum(
dfs(pos + 1, (st + (i & 1 ^ 1)) % 2, lim and i == up)
for i in range(up + 1)
)
s = str(l - 1)
a = dfs(0, 0, True)
dfs.cache_clear()
s = str(r)
b = dfs(0, 0, True)
return b - aclass Solution {
String s;
long[][][] f;
public long countEvenlyGoodIntegers(long l, long r) {
return calc(r) - calc(l - 1);
}
private long calc(long x) {
s = String.valueOf(x);
f = new long[s.length()][2][2];
for (long[][] a : f) {
for (long[] b : a) {
Arrays.fill(b, -1);
}
}
return dfs(0, 0, true);
}
private long dfs(int pos, int st, boolean lim) {
if (pos >= s.length()) {
return st ^ 1;
}
int k = lim ? 1 : 0;
if (f[pos][st][k] != -1) {
return f[pos][st][k];
}
int up = lim ? s.charAt(pos) - '0' : 9;
long res = 0;
for (int i = 0; i <= up; ++i) {
res += dfs(pos + 1, (st + (i & 1 ^ 1)) % 2, lim && i == up);
}
return f[pos][st][k] = res;
}
}class Solution {
string s;
long long f[20][2][2];
long long dfs(int pos, int st, bool lim) {
if (pos >= s.size()) {
return st ^ 1;
}
if (f[pos][st][lim] != -1) {
return f[pos][st][lim];
}
int up = lim ? s[pos] - '0' : 9;
long long res = 0;
for (int i = 0; i <= up; ++i) {
res += dfs(pos + 1, (st + (i & 1 ^ 1)) % 2, lim && i == up);
}
return f[pos][st][lim] = res;
}
long long calc(long long x) {
s = to_string(x);
memset(f, -1, sizeof(f));
return dfs(0, 0, true);
}
public:
long long countEvenlyGoodIntegers(long long l, long long r) {
return calc(r) - calc(l - 1);
}
};function countEvenlyGoodIntegers(l: number, r: number): number {
let s: string;
let f: number[][][];
const calc = (x: number): number => {
s = String(x);
f = Array.from({ length: s.length }, () =>
Array.from({ length: 2 }, () => Array(2).fill(-1)),
);
const dfs = (pos: number, st: number, lim: boolean): number => {
if (pos >= s.length) {
return st ^ 1;
}
const k = lim ? 1 : 0;
if (f[pos][st][k] !== -1) {
return f[pos][st][k];
}
const up = lim ? Number(s[pos]) : 9;
let res = 0;
for (let i = 0; i <= up; ++i) {
res += dfs(pos + 1, (st + ((i & 1) ^ 1)) % 2, lim && i === up);
}
return (f[pos][st][k] = res);
};
return dfs(0, 0, true);
};
return calc(r) - calc(l - 1);
}func countEvenlyGoodIntegers(l int64, r int64) int64 {
var s string
var f [20][2][2]int64
calc := func(x int64) int64 {
s = strconv.FormatInt(x, 10)
for i := range f {
for j := range f[i] {
for k := range f[i][j] {
f[i][j][k] = -1
}
}
}
var dfs func(pos, st int, lim bool) int64
dfs = func(pos, st int, lim bool) int64 {
if pos >= len(s) {
return int64(st ^ 1)
}
k := 0
if lim {
k = 1
}
if f[pos][st][k] != -1 {
return f[pos][st][k]
}
up := 9
if lim {
up = int(s[pos] - '0')
}
var res int64
for i := 0; i <= up; i++ {
res += dfs(pos+1, (st+(i&1^1))%2, lim && i == up)
}
f[pos][st][k] = res
return res
}
return dfs(0, 0, true)
}
return calc(r) - calc(l-1)
}Complexity analysis
| Approach | Time | Space | When to Use |
|---|---|---|---|
| Brute Force Enumeration | O(N * d) | O(1) | Small bounds, verifying results |
| Digit DP (Memoized) | O(d * S * 10) | O(d * S) | General case with large bounds |
| Digit DP (Bottom-Up) | O(d * S * 10) | O(S) | Memory constrained or no recursion allowed |
Frequently asked questions
Count Evenly Good Integers Python solution?
In Python, convert the upper bound to a string to get its digits, then define a recursive function with @lru_cache over (position, summary, tight). Iterate over digits 0 to 9, respect the upper bound when tight is true, and update the summary for each choice. Subtract the count for L - 1 from the count for R to get the range answer.
Is Count Evenly Good Integers easy or hard?
It is rated Hard on FleetCode, with an acceptance rate of about 79.6 percent. The difficulty comes from designing the digit DP state correctly rather than from the code itself. Once the state is clear, the implementation is a standard memoized recursion.
How to solve Count Evenly Good Integers in O(log N)?
Use digit DP. The number of digits is log10(N), so the position dimension is O(log N), and the transitions per state are constant (at most 10 digit choices). The total work is O(log N * S), where S is the size of the summary state. Memoization ensures each (position, summary, tight) combination is computed only once.
What is the best approach for Count Evenly Good Integers?
Digit DP is the best approach. It processes the number digit by digit, tracks a tight-bound flag and a compact summary of the digits seen so far, and memoizes each state. This runs in O(d * S * 10) time, where d is the digit count and S is the number of summary states, which stays fast even for bounds near 10^18.
What data structure is used in Count Evenly Good Integers?
The main structure is a memoization table, usually a multidimensional array indexed by digit position, summary state, and tight flag, or a hash map keyed by those values. Digits are extracted from the bound with integer division and modulo by 10, so no string or list is strictly required.
What is the time complexity of Count Evenly Good Integers?
The optimal digit DP solution runs in O(d * S * 10) time, where d is the number of digits in the bound and S is the number of distinct summary states. The brute force enumeration runs in O(N * d) time, where N is the size of the range, which is too slow for large inputs.
Is Count Evenly Good Integers asked at Google, Amazon, or Meta?
The FleetCode listing tags this problem under General and does not list specific company associations. Digit DP problems appear regularly in interviews at large tech companies, so practicing the pattern is useful regardless of which company you target.