Count Stepping Numbers in Range solution and explanation
7 more videos on this problem
2801. Count Stepping Numbers in Range | Digit DP Explained (again :D)codingMohan · 37:13 · 1K views
Leetcode Weekly contest 356 - Hard - Count Stepping Numbers in RangePrakhar Agrawal · 19:00 · 1K views
2801. Count Stepping Numbers in Range (Leetcode Hard)Programming Live with Larry · 1:05:05 · 528 views
2801. Count Stepping Numbers in Range | Leetcode Hardquebeat · 27:27 · 442 views
【每日一题】LeetCode 2801. Count Stepping Numbers in RangeHuifeng Guan · 21:25 · 265 views
[Eng] Leetcode Weekly Contest 356 | 2801 Count Stepping Numbers in RangeCyCode · 20:36 · 255 views
A Deep Dive into a LeetCode 2801. Count Stepping Numbers in Range with PythonvanAmsen · 7:32 · 131 views
leetcode 2801. Count Stepping Numbers in Rangeprogramming on the couch · 31:29 · 52 views
The problem
Given two positive integers low and high represented as strings, find the count of stepping numbers in the inclusive range [low, high].
A stepping number is an integer such that all of its adjacent digits have an absolute difference of exactly 1.
Return an integer denoting the count of stepping numbers in the inclusive range [low, high].
Since the answer may be very large, return it modulo 109 + 7.
Note: A stepping number should not have a leading zero.
Example 1:
Input: low = "1", high = "11" Output: 10 Explanation: The stepping numbers in the range [1,11] are 1, 2, 3, 4, 5, 6, 7, 8, 9 and 10. There are a total of 10 stepping numbers in the range. Hence, the output is 10.
Example 2:
Input: low = "90", high = "101" Output: 2 Explanation: The stepping numbers in the range [90,101] are 98 and 101. There are a total of 2 stepping numbers in the range. Hence, the output is 2.
Constraints:
1 <= int(low) <= int(high) < 101001 <= low.length, high.length <= 100lowandhighconsist of only digits.lowandhighdon't have any leading zeros.
Approach overview
Problem Overview: Given two numeric strings low and high, count how many integers in this inclusive range are stepping numbers. A stepping number is a number where the absolute difference between every pair of adjacent digits is exactly 1 (e.g., 123 or 321). The challenge is that the range can be extremely large, so iterating through every number is not always feasible.
Approach 1: Brute Force Method (Time: O(N * d), Space: O(1))
The straightforward strategy is to iterate through every number between low and high. Convert each number to a string and check whether every adjacent digit pair differs by exactly 1. This involves a simple scan of the digits using a loop and comparing abs(d[i] - d[i-1]). If the condition holds for all adjacent digits, increment the count. The algorithm is easy to implement and useful for understanding the definition of stepping numbers, but it becomes impractical when the numeric range is large because the iteration cost grows linearly with the size of the range.
Approach 2: Optimized Pair Calculation Using Single Loop (Digit DP) (Time: O(d * 10 * 2), Space: O(d * 10))
A scalable solution uses dynamic programming with digit constraints (digit DP). Instead of enumerating every number, compute how many valid stepping numbers exist up to a given bound. Process the number digit by digit, keeping track of the previous digit and whether the current prefix is already smaller than the bound. From each digit, the next allowed digits are only prev - 1 or prev + 1. Memoization stores intermediate states defined by position, previous digit, and tight-bound state. Run this counting function for high and subtract the count for low - 1. This reduces the search space dramatically and relies heavily on digit-level state transitions using strings and string processing.
Recommended for interviews: Interviewers expect the optimized digit DP approach. The brute force method demonstrates that you understand what defines a stepping number, but it fails on large ranges. The DP solution shows mastery of constrained digit generation, memoization, and range counting—skills commonly tested in hard problems involving numeric strings.
Approach 1: Approach 1: Brute Force Method
This approach involves examining all possible combinations to find the solution by iterating over each element. While simple, the method may not be efficient for large datasets due to its time complexity.
def brute_force_solution(arr):
total_sum = 0
for i in range(len(arr) - 1):
for j in range(i + 1, len(arr)):
total_sum += arr[i] + arr[j]
return total_sum
arr = [1, 2, 3, 4]
print("Sum of all pairs:", brute_force_solution(arr))public class BruteForceSolution {
public static int sumOfPairs(int[] arr) {
int sum = 0;
for (int i = 0; i < arr.length - 1; i++) {
for (int j = i + 1; j < arr.length; j++) {
sum += arr[i] + arr[j];
}
}
return sum;
}
public static void main(String[] args) {
int[] arr = {1, 2, 3, 4};
System.out.println("Sum of all pairs: " + sumOfPairs(arr));
}
}#include <iostream>
#include <vector>
int bruteForceSolution(const std::vector<int>& arr) {
int sum = 0;
for (size_t i = 0; i < arr.size() - 1; ++i) {
for (size_t j = i + 1; j < arr.size(); ++j) {
sum += arr[i] + arr[j];
}
}
return sum;
}
int main() {
std::vector<int> arr = {1, 2, 3, 4};
std::cout << "Sum of all pairs: " << bruteForceSolution(arr) << std::endl;
return 0;
}function sumOfPairs(arr) {
let sum = 0;
for (let i = 0; i < arr.length - 1; i++) {
for (let j = i + 1; j < arr.length; j++) {
sum += arr[i] + arr[j];
}
}
return sum;
}
let arr = [1, 2, 3, 4];
console.log("Sum of all pairs:", sumOfPairs(arr));#include <stdio.h>
int bruteForceSolution(int arr[], int n) {
int sum = 0;
for (int i = 0; i < n - 1; i++) {
for (int j = i + 1; j < n; j++) {
// Perform a calculation (e.g., sum)
sum += arr[i] + arr[j];
}
}
return sum;
}
int main() {
int arr[] = {1, 2, 3, 4};
int n = sizeof(arr) / sizeof(arr[0]);
printf("Sum of all pairs: %d\n", bruteForceSolution(arr, n));
return 0;
}using System;
public class BruteForceSolution {
public static int SumOfPairs(int[] arr) {
int sum = 0;
for (int i = 0; i < arr.Length - 1; i++) {
for (int j = i + 1; j < arr.Length; j++) {
sum += arr[i] + arr[j];
}
}
return sum;
}
public static void Main() {
int[] arr = {1, 2, 3, 4};
Console.WriteLine("Sum of all pairs: " + SumOfPairs(arr));
}
}This Python function performs a brute force calculation by summing all possible pairs of an integer list.
Time Complexity: O(n^2)
Space Complexity: O(1)
Approach 2: Approach 2: Optimized Pair Calculation Using Single Loop
This approach improves efficiency by leveraging a mathematical observation or data structure to avoid redundant calculations, significantly reducing time complexity.
def optimized_solution(arr):
total_sum = sum(arr)
sum_of_pairs = total_sum * (len(arr) - 1)
return sum_of_pairs
arr = [1, 2, 3, 4]
print("Optimized sum of all pairs:", optimized_solution(arr))public class OptimizedSolution {
public static int optimizedSumOfPairs(int[] arr) {
int sum = 0;
int totalSum = 0;
for (int num : arr) {
totalSum += num;
}
for (int num : arr) {
sum += num * (arr.length - 1);
}
return sum;
}
public static void main(String[] args) {
int[] arr = {1, 2, 3, 4};
System.out.println("Optimized sum of all pairs: " + optimizedSumOfPairs(arr));
}
}#include <iostream>
#include <vector>
int optimizedSolution(const std::vector<int>& arr) {
int sum = 0;
int totalSum = 0;
for (int num : arr) {
totalSum += num;
}
for (int num : arr) {
sum += num * (arr.size() - 1);
}
return sum;
}
int main() {
std::vector<int> arr = {1, 2, 3, 4};
std::cout << "Optimized sum of all pairs: " << optimizedSolution(arr) << std::endl;
return 0;
}function optimizedSumOfPairs(arr) {
let totalSum = arr.reduce((a, b) => a + b, 0);
let sumOfPairs = totalSum * (arr.length - 1);
return sumOfPairs;
}
let arr = [1, 2, 3, 4];
console.log("Optimized sum of all pairs:", optimizedSumOfPairs(arr));#include <stdio.h>
int optimizedSolution(int arr[], int n) {
int sum = 0;
int totalSum = 0;
for (int i = 0; i < n; i++) {
totalSum += arr[i];
}
for (int i = 0; i < n; i++) {
sum += arr[i] * (n - 1);
}
return sum;
}
int main() {
int arr[] = {1, 2, 3, 4};
int n = sizeof(arr) / sizeof(arr[0]);
printf("Optimized sum of all pairs: %d\n", optimizedSolution(arr, n));
return 0;
}using System;
public class OptimizedSolution {
public static int OptimizedSumOfPairs(int[] arr) {
int sum = 0;
int totalSum = 0;
for (int i = 0; i < arr.Length; i++) {
totalSum += arr[i];
}
for (int i = 0; i < arr.Length; i++) {
sum += arr[i] * (arr.Length - 1);
}
return sum;
}
public static void Main() {
int[] arr = {1, 2, 3, 4};
Console.WriteLine("Optimized sum of all pairs: " + OptimizedSumOfPairs(arr));
}
}This Python function uses a mathematical inference to calculate the sum of pairs by factorizing the total sum, achieving linear complexity.
Time Complexity: O(n)
Space Complexity: O(1)
Approach 3: Digit DP
We notice that the problem is asking for the number of stepping numbers in the interval [low, high]. For such an interval [l,..r] problem, we can usually consider transforming it into finding the answers for [1, r] and [1, l-1], and then subtracting the latter from the former. Moreover, the problem only involves the relationship between different digits, not the specific values, so we can consider using Digit DP to solve it.
We design a function dfs(pos, pre, lead, limit), which represents the number of schemes when we are currently processing the pos-th digit, the previous digit is pre, whether the current number only contains leading zeros is lead, and whether the current number has reached the upper limit is limit. The range of pos is [0, len(num)).
The execution logic of the function dfs(pos, pre, lead, limit) is as follows:
If pos exceeds the length of num, it means that we have processed all the digits. If lead is true at this time, it means that the current number only contains leading zeros and is not a valid number. We can return 0 to indicate that the number of schemes is 0; otherwise, we return 1 to indicate that the number of schemes is 1.
Otherwise, we calculate the upper limit up of the current digit, and then enumerate the digit i in the range [0,..up]:
- If
i=0andleadis true, it means that the current number only contains leading zeros. We recursively calculate the value ofdfs(pos+1,pre, true, limit\ and\ i=up)and add it to the answer. - Otherwise, if
preis-1, or the absolute difference betweeniandpreis1, it means that the current number is a valid stepping number. We recursively calculate the value ofdfs(pos+1,i, false, limit\ and\ i=up)and add it to the answer.
Finally, we return the answer.
In the main function, we calculate the answers a and b for [1, high] and [1, low-1] respectively. The final answer is a-b. Note the modulo operation of the answer.
The time complexity is O(log M times |\Sigma|^2), and the space complexity is O(log M times |\Sigma|), where M represents the size of the number high, and |\Sigma| represents the digit set.
Similar problems:
function countSteppingNumbers(low: string, high: string): number {
const mod = 1e9 + 7;
const m = high.length;
let f: number[][] = Array(m + 1)
.fill(0)
.map(() => Array(10).fill(-1));
let num = high;
const dfs = (pos: number, pre: number, lead: boolean, limit: boolean): number => {
if (pos >= num.length) {
return lead ? 0 : 1;
}
if (!lead && !limit && f[pos][pre] !== -1) {
return f[pos][pre];
}
let ans = 0;
const up = limit ? +num[pos] : 9;
for (let i = 0; i <= up; i++) {
if (i == 0 && lead) {
ans += dfs(pos + 1, pre, true, limit && i == up);
} else if (pre == -1 || Math.abs(pre - i) == 1) {
ans += dfs(pos + 1, i, false, limit && i == up);
}
ans %= mod;
}
if (!lead && !limit) {
f[pos][pre] = ans;
}
return ans;
};
const a = dfs(0, -1, true, true);
num = (BigInt(low) - 1n).toString();
f = Array(m + 1)
.fill(0)
.map(() => Array(10).fill(-1));
const b = dfs(0, -1, true, true);
return (a - b + mod) % mod;
}func countSteppingNumbers(low string, high string) int {
const mod = 1e9 + 7
f := [110][10]int{}
for i := range f {
for j := range f[i] {
f[i][j] = -1
}
}
num := high
var dfs func(int, int, bool, bool) int
dfs = func(pos, pre int, lead bool, limit bool) int {
if pos >= len(num) {
if lead {
return 0
}
return 1
}
if !lead && !limit && f[pos][pre] != -1 {
return f[pos][pre]
}
var ans int
up := 9
if limit {
up = int(num[pos] - '0')
}
for i := 0; i <= up; i++ {
if i == 0 && lead {
ans += dfs(pos+1, pre, true, limit && i == up)
} else if pre == -1 || abs(pre-i) == 1 {
ans += dfs(pos+1, i, false, limit && i == up)
}
ans %= mod
}
if !lead && !limit {
f[pos][pre] = ans
}
return ans
}
a := dfs(0, -1, true, true)
t := []byte(low)
for i := len(t) - 1; i >= 0; i-- {
if t[i] != '0' {
t[i]--
break
}
t[i] = '9'
}
num = string(t)
f = [110][10]int{}
for i := range f {
for j := range f[i] {
f[i][j] = -1
}
}
b := dfs(0, -1, true, true)
return (a - b + mod) % mod
}
func abs(x int) int {
if x < 0 {
return -x
}
return x
}Complexity comparison
| Approach | Complexity |
|---|---|
| Approach 1: Brute Force Method | Time Complexity: O(n^2) |
| Approach 2: Optimized Pair Calculation Using Single Loop | Time Complexity: O(n) |
| Digit DP | – |
Complexity analysis
| Approach | Time | Space | When to Use |
|---|---|---|---|
| Brute Force Method | O(N * d) | O(1) | Small numeric ranges where iterating every number is feasible |
| Optimized Pair Calculation Using Single Loop (Digit DP) | O(d * 10 * 2) | O(d * 10) | Large ranges with big integers where enumeration is impossible |
Frequently asked questions
Is Count Stepping Numbers in Range easy or hard?
Count Stepping Numbers in Range is classified as a Hard problem on LeetCode. The difficulty comes from handling extremely large ranges and applying digit dynamic programming correctly. Understanding tight constraints, memoization states, and digit transitions is essential to solving it efficiently.
Count Stepping Numbers in Range Python/Java solution
Python and Java solutions typically implement a recursive or iterative digit DP with memoization. The function processes digits of the bound string, tracks the previous digit, and enforces the stepping constraint. The final answer is computed by evaluating the DP for high and subtracting the result for low minus one.
How to solve Count Stepping Numbers in Range in O(n)?
Use a digit DP strategy that builds numbers digit by digit while enforcing the stepping condition. Track the previous digit and only transition to prev−1 or prev+1. Memoize states based on index, previous digit, and tight flag to avoid recomputation. This converts an exponential search into a linear scan over digit states.
What is the best approach for Count Stepping Numbers in Range?
Digit Dynamic Programming (Digit DP) is the most efficient approach. Instead of checking every number in the range, the algorithm counts valid stepping numbers up to a bound using digit-by-digit transitions. The final answer is computed as count(high) minus count(low − 1). This reduces the complexity to roughly O(d * 10) where d is the number of digits.
Is Count Stepping Numbers in Range asked at Google/Amazon/Meta?
Problems based on digit DP and constrained number generation appear frequently in interviews at companies like Google, Amazon, and Meta. Variants of stepping numbers, counting valid integers in ranges, and digit constraint problems are common in hard-level interview rounds.
What data structure is used in Count Stepping Numbers in Range?
The core technique uses dynamic programming with memoization. States are typically stored in a DP table or hash map keyed by digit index, previous digit, and tight constraint. Strings are also used to process the numeric bounds safely when the values exceed normal integer limits.
What is the time complexity of Count Stepping Numbers in Range?
The optimized digit DP solution runs in O(d * 10 * 2) time where d is the number of digits in the bound. Each state represents a digit position, previous digit, and tight constraint. The brute force approach is much slower at O(N * d) because it checks every number in the range.
