Skip to content

3791. Number of Balanced Integers in a Range

Description

You are given two integers low and high.

An integer is called balanced if it satisfies both of the following conditions:

  • It contains at least two digits.
  • The sum of digits at even positions is equal to the sum of digits at odd positions (the leftmost digit has position 1).

Return an integer representing the number of balanced integers in the range [low, high] (both inclusive).

 

Example 1:

Input: low = 1, high = 100

Output: 9

Explanation:

The 9 balanced numbers between 1 and 100 are 11, 22, 33, 44, 55, 66, 77, 88, and 99.

Example 2:

Input: low = 120, high = 129

Output: 1

Explanation:

Only 121 is balanced because the sum of digits at even and odd positions are both 2.

Example 3:

Input: low = 1234, high = 1234

Output: 0

Explanation:

1234 is not balanced because the sum of digits at odd positions (1 + 3 = 4) does not equal the sum at even positions (2 + 4 = 6).

 

Constraints:

  • 1 <= low <= high <= 1015

Solutions

Solution 1: Digit DP

First, if \(\textit{high} < 11\), there are no balanced integers in the range, so we directly return \(0\). Otherwise, we update \(\textit{low}\) to \(\max(\textit{low}, 11)\).

Then we design a function \(\textit{dfs}(\textit{pos}, \textit{diff}, \textit{lim})\), which represents processing the \(\textit{pos}\)-th digit of the number, where \(\textit{diff}\) is the difference between the sum of digits at odd positions and the sum of digits at even positions, and \(\textit{lim}\) indicates whether the current digit is constrained by the upper bound. The function returns the number of balanced integers that can be constructed from the current state.

The execution logic of the function is as follows:

  • If \(\textit{pos}\) exceeds the length of the number, it means all digits have been processed. If \(\textit{diff} = 0\), the current number is a balanced integer, return \(1\); otherwise, return \(0\).
  • Calculate the upper bound \(\textit{up}\) for the current digit. If constrained, it equals the current digit of the number; otherwise, it is \(9\).
  • Iterate through all possible digits \(i\) for the current position. For each digit \(i\), recursively call \(\textit{dfs}(\textit{pos} + 1, \textit{diff} + i \times (\text{1 if pos \% 2 == 0 else -1}), \textit{lim} \&\& i == \textit{up})\), and accumulate the results.
  • Return the accumulated result.

We first calculate the number of balanced integers \(\textit{a}\) in the range \([1, \textit{low} - 1]\), then calculate the number of balanced integers \(\textit{b}\) in the range \([1, \textit{high}]\), and finally return \(\textit{b} - \textit{a}\).

To avoid redundant calculations, we use memoization to store previously computed states.

The time complexity is \(O(\log^2 M \times D^2)\), and the space complexity is \(O(\log^2 M \times D)\). Here, \(M\) is the value of \(\textit{high}\), and \(D = 10\).

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Solution:
    def countBalanced(self, low: int, high: int) -> int:
        @cache
        def dfs(pos: int, diff: int, lim: int) -> int:
            if pos >= len(num):
                return 1 if diff == 0 else 0
            res = 0
            up = int(num[pos]) if lim else 9
            for i in range(up + 1):
                res += dfs(
                    pos + 1, diff + i * (1 if pos % 2 == 0 else -1), lim and i == up
                )
            return res

        if high < 11:
            return 0
        low = max(low, 11)
        num = str(low - 1)
        a = dfs(0, 0, True)
        dfs.cache_clear()
        num = str(high)
        b = dfs(0, 0, True)
        return b - a
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
class Solution {
    private char[] num;
    private Long[][] f;
    private final int base = 90;

    public long countBalanced(long low, long high) {
        if (high < 11) {
            return 0;
        }
        low = Math.max(low, 11);
        num = String.valueOf(low - 1).toCharArray();
        f = new Long[num.length][base << 1 | 1];
        long a = dfs(0, 0, true);
        num = String.valueOf(high).toCharArray();
        f = new Long[num.length][base << 1 | 1];
        long b = dfs(0, 0, true);
        return b - a;
    }

    private long dfs(int pos, int diff, boolean lim) {
        if (pos >= num.length) {
            return diff == 0 ? 1 : 0;
        }
        if (!lim && f[pos][diff + base] != null) {
            return f[pos][diff + base];
        }
        int up = lim ? num[pos] - '0' : 9;
        long res = 0;
        for (int i = 0; i <= up; ++i) {
            res += dfs(pos + 1, diff + i * (pos % 2 == 0 ? 1 : -1), lim && i == up);
        }
        if (!lim) {
            f[pos][diff + base] = res;
        }
        return res;
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class Solution {
public:
    string num;
    long long f[20][181];
    static constexpr int base = 90;

    long long dfs(int pos, int diff, bool lim) {
        if (pos >= (int) num.size()) {
            return diff == 0 ? 1LL : 0LL;
        }
        if (!lim && f[pos][diff + base] != -1) {
            return f[pos][diff + base];
        }
        int up = lim ? num[pos] - '0' : 9;
        long long res = 0;
        for (int i = 0; i <= up; ++i) {
            res += dfs(pos + 1, diff + i * (pos % 2 == 0 ? 1 : -1), lim && i == up);
        }
        if (!lim) {
            f[pos][diff + base] = res;
        }
        return res;
    }

    long long countBalanced(long long low, long long high) {
        if (high < 11) {
            return 0;
        }
        low = max(low, 11LL);

        num = to_string(low - 1);
        memset(f, -1, sizeof(f));
        long long a = dfs(0, 0, true);

        num = to_string(high);
        memset(f, -1, sizeof(f));
        long long b = dfs(0, 0, true);

        return b - a;
    }
};
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
func countBalanced(low int64, high int64) int64 {
    if high < 11 {
        return 0
    }
    if low < 11 {
        low = 11
    }
    const base = 90

    var num []byte
    var f [20][181]int64

    var dfs func(pos int, diff int, lim bool) int64
    dfs = func(pos int, diff int, lim bool) int64 {
        if pos >= len(num) {
            if diff == 0 {
                return 1
            }
            return 0
        }
        if !lim && f[pos][diff+base] != -1 {
            return f[pos][diff+base]
        }
        up := 9
        if lim {
            up = int(num[pos] - '0')
        }
        var res int64 = 0
        for i := 0; i <= up; i++ {
            if pos%2 == 0 {
                res += dfs(pos+1, diff+i, lim && i == up)
            } else {
                res += dfs(pos+1, diff-i, lim && i == up)
            }
        }
        if !lim {
            f[pos][diff+base] = res
        }
        return res
    }

    num = []byte(fmt.Sprint(low - 1))
    for i := range f {
        for j := range f[i] {
            f[i][j] = -1
        }
    }
    a := dfs(0, 0, true)

    num = []byte(fmt.Sprint(high))
    for i := range f {
        for j := range f[i] {
            f[i][j] = -1
        }
    }
    b := dfs(0, 0, true)

    return b - a
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
function countBalanced(low: number, high: number): number {
    if (high < 11) {
        return 0;
    }
    if (low < 11) {
        low = 11;
    }
    const base = 90;

    let num: string;
    let f: number[][];

    function dfs(pos: number, diff: number, lim: boolean): number {
        if (pos >= num.length) {
            return diff === 0 ? 1 : 0;
        }
        if (!lim && f[pos][diff + base] !== -1) {
            return f[pos][diff + base];
        }
        const up = lim ? num.charCodeAt(pos) - 48 : 9;
        let res = 0;
        for (let i = 0; i <= up; ++i) {
            res += dfs(pos + 1, diff + i * (pos % 2 === 0 ? 1 : -1), lim && i === up);
        }
        if (!lim) {
            f[pos][diff + base] = res;
        }
        return res;
    }

    num = String(low - 1);
    f = Array.from({ length: num.length }, () => Array((base << 1) | 1).fill(-1));
    const a = dfs(0, 0, true);

    num = String(high);
    f = Array.from({ length: num.length }, () => Array((base << 1) | 1).fill(-1));
    const b = dfs(0, 0, true);

    return b - a;
}

Comments