Given a number n, count the numbers from 1 to n that don't contain digit d in their decimal representation.
Examples:
Input: n = 25, d = 3
Output: 22
Explanation: From 1 to 25, the numbers 3, 13, and 23 contain the digit 3, so the answer is 25 - 3 = 22.Input: n = 5, d = 3
Output: 4
Explanation: From 1 to 5, only 3 contains the digit 3, so the count of numbers without digit 3 is 4.
Table of Content
[Naive Approach] Check Every Number Individually - O(n log n) Time and O(1) Space
Every number from 1 to n can be checked directly by repeatedly extracting its last digit via modulo 10 and comparing it against d, dividing by 10 each time to move to the next digit. If none of a number's digits match d, it is counted. Since checking a single number takes time proportional to its number of digits (roughly log n), and this check is repeated for every number up to n, the total work scales with n times its digit count, which becomes far too slow once n reaches values like 10^9.
Illustration:
- Take n = 25, d = 3.
- Check each number from 1 to 25: for a number like 13, extract its digits (3, then 1), and since 3 matches d, it is excluded.
- Similarly, 3 and 23 are excluded for the same reason.
- Every other number's digits are checked the same way and found not to contain 3, so they are counted.
- Out of 25 numbers checked, 3 are excluded, leaving 22.
#include <bits/stdc++.h>
using namespace std;
bool containsDigit(int num, int d) {
// 0 itself only contains digit 0
if (num == 0)
return d == 0;
while (num > 0) {
if (num % 10 == d)
return true;
num /= 10;
}
return false;
}
int countWithout(int n, int d) {
int count = 0;
for (int i = 1; i <= n; i++) {
if (!containsDigit(i, d))
count++;
}
return count;
}
int main() {
int n = 25, d = 3;
cout << countWithout(n, d) << endl;
return 0;
}
class GfG {
static boolean containsDigit(int num, int d) {
// 0 itself only contains digit 0
if (num == 0)
return d == 0;
while (num > 0) {
if (num % 10 == d)
return true;
num /= 10;
}
return false;
}
static int countWithout(int n, int d) {
int count = 0;
for (int i = 1; i <= n; i++) {
if (!containsDigit(i, d))
count++;
}
return count;
}
public static void main(String[] args) {
int n = 25, d = 3;
System.out.println(countWithout(n, d));
}
}
def containsDigit(num, d):
# 0 itself only contains digit 0
if num == 0:
return d == 0
while num > 0:
if num % 10 == d:
return True
num //= 10
return False
def countWithout(n, d):
count = 0
for i in range(1, n + 1):
if not containsDigit(i, d):
count += 1
return count
n, d = 25, 3
print(countWithout(n, d))
using System;
class GfG {
static bool containsDigit(int num, int d) {
// 0 itself only contains digit 0
if (num == 0)
return d == 0;
while (num > 0) {
if (num % 10 == d)
return true;
num /= 10;
}
return false;
}
static int countWithout(int n, int d) {
int count = 0;
for (int i = 1; i <= n; i++) {
if (!containsDigit(i, d))
count++;
}
return count;
}
static void Main() {
int n = 25, d = 3;
Console.WriteLine(countWithout(n, d));
}
}
function containsDigit(num, d) {
// 0 itself only contains digit 0
if (num === 0)
return d === 0;
while (num > 0) {
if (num % 10 === d)
return true;
num = Math.floor(num / 10);
}
return false;
}
function countWithout(n, d) {
let count = 0;
for (let i = 1; i <= n; i++) {
if (!containsDigit(i, d))
count++;
}
return count;
}
// Driver Code
const n = 25, d = 3;
console.log(countWithout(n, d));
Output
22
[Expected Approach] Digit DP - O(log n) Time and O(log n) Space
Numbers are built digit by digit, tracking whether the digits so far are still bounded by n (tight) and whether a non-zero digit has started (to correctly handle digit-0 leading-zero cases). At each position every valid digit is tried, skipping digit d entirely once the number has started, and results are memoized per state so each is computed once. The final count directly gives the numbers not containing d.
Illustration:
- Take n = 25, d = 3.
- Building 2-digit numbers position by position: at the first position, digits 0 through 2 are tried (since n's first digit is 2, and the tight bound restricts the choice at this position only while every prior digit has exactly matched n so far).
- At the first position, digit 3 would give numbers 30-39, but the tight bound only allows digits 0 through 2 here, so this digit never comes up in this branch anyway.
- At the second position, whenever the first digit chosen is 0, 1, or 2, digit 3 is skipped outright whenever it's tried, so numbers like 3, 13, and 23 are never counted, directly excluding exactly the three numbers that contain digit 3.
- This leaves the 22 valid numbers from 1 to 25 that never use digit 3.
#include <bits/stdc++.h>
using namespace std;
int countWithout(int n, int d) {
if (n <= 0)
return 0;
string s = to_string(n);
int length = s.length();
// dp[tight][started] = count of valid completions from the current position onward
int dp[2][2];
// base case: at the end, a number counts only if it actually started (non-empty)
for (int tight = 0; tight < 2; tight++)
for (int started = 0; started < 2; started++)
dp[tight][started] = started;
// build the table backward from the last digit position to the first
for (int pos = length - 1; pos >= 0; pos--) {
int newDp[2][2] = {0};
for (int tight = 0; tight < 2; tight++) {
for (int started = 0; started < 2; started++) {
int limit = tight ? (s[pos] - '0') : 9;
int total = 0;
// try every valid digit, skipping d once the number has started
for (int digit = 0; digit <= limit; digit++) {
int willStart = started || (digit != 0);
if (willStart && digit == d)
continue;
int newTight = tight && (digit == limit);
total += dp[newTight][willStart];
}
newDp[tight][started] = total;
}
}
memcpy(dp, newDp, sizeof(dp));
}
return dp[1][0];
}
int main() {
int n = 25, d = 3;
cout << countWithout(n, d) << endl;
return 0;
}
class GfG {
static int countWithout(int n, int d) {
if (n <= 0)
return 0;
String s = String.valueOf(n);
int length = s.length();
// dp[tight][started] = count of valid completions from the current position onward
int[][] dp = new int[2][2];
// base case: at the end, a number counts only if it actually started (non-empty)
for (int tight = 0; tight < 2; tight++)
for (int started = 0; started < 2; started++)
dp[tight][started] = started;
// build the table backward from the last digit position to the first
for (int pos = length - 1; pos >= 0; pos--) {
int[][] newDp = new int[2][2];
for (int tight = 0; tight < 2; tight++) {
for (int started = 0; started < 2; started++) {
int limit = (tight == 1) ? (s.charAt(pos) - '0') : 9;
int total = 0;
// try every valid digit, skipping d once the number has started
for (int digit = 0; digit <= limit; digit++) {
int willStart = (started == 1 || digit != 0) ? 1 : 0;
if (willStart == 1 && digit == d)
continue;
int newTight = (tight == 1 && digit == limit) ? 1 : 0;
total += dp[newTight][willStart];
}
newDp[tight][started] = total;
}
}
dp = newDp;
}
return dp[1][0];
}
public static void main(String[] args) {
int n = 25, d = 3;
System.out.println(countWithout(n, d));
}
}
def countWithout(n, d):
if n <= 0:
return 0
s = str(n)
length = len(s)
# dp[tight][started] = count of valid completions from the current position onward
dp = [[0] * 2 for _ in range(2)]
# base case: at the end, a number counts only if it actually started (non-empty)
for tight in range(2):
for started in range(2):
dp[tight][started] = started
# build the table backward from the last digit position to the first
for pos in range(length - 1, -1, -1):
newDp = [[0] * 2 for _ in range(2)]
for tight in range(2):
for started in range(2):
limit = int(s[pos]) if tight else 9
total = 0
# try every valid digit, skipping d once the number has started
for digit in range(0, limit + 1):
willStart = 1 if (started or digit != 0) else 0
if willStart and digit == d:
continue
newTight = 1 if (tight and digit == limit) else 0
total += dp[newTight][willStart]
newDp[tight][started] = total
dp = newDp
return dp[1][0]
n, d = 25, 3
print(countWithout(n, d))
using System;
class GfG {
static int countWithout(int n, int d) {
if (n <= 0)
return 0;
string s = n.ToString();
int length = s.Length;
// dp[tight][started] = count of valid completions from the current position onward
int[,] dp = new int[2, 2];
// base case: at the end, a number counts only if it actually started (non-empty)
for (int tight = 0; tight < 2; tight++)
for (int started = 0; started < 2; started++)
dp[tight, started] = started;
// build the table backward from the last digit position to the first
for (int pos = length - 1; pos >= 0; pos--) {
int[,] newDp = new int[2, 2];
for (int tight = 0; tight < 2; tight++) {
for (int started = 0; started < 2; started++) {
int limit = (tight == 1) ? (s[pos] - '0') : 9;
int total = 0;
// try every valid digit, skipping d once the number has started
for (int digit = 0; digit <= limit; digit++) {
int willStart = (started == 1 || digit != 0) ? 1 : 0;
if (willStart == 1 && digit == d)
continue;
int newTight = (tight == 1 && digit == limit) ? 1 : 0;
total += dp[newTight, willStart];
}
newDp[tight, started] = total;
}
}
dp = newDp;
}
return dp[1, 0];
}
static void Main() {
int n = 25, d = 3;
Console.WriteLine(countWithout(n, d));
}
}
function countWithout(n, d) {
if (n <= 0)
return 0;
const s = n.toString();
const length = s.length;
// dp[tight][started] = count of valid completions from the current position onward
let dp = [[0, 0], [0, 0]];
// base case: at the end, a number counts only if it actually started (non-empty)
for (let tight = 0; tight < 2; tight++)
for (let started = 0; started < 2; started++)
dp[tight][started] = started;
// build the table backward from the last digit position to the first
for (let pos = length - 1; pos >= 0; pos--) {
const newDp = [[0, 0], [0, 0]];
for (let tight = 0; tight < 2; tight++) {
for (let started = 0; started < 2; started++) {
const limit = tight ? parseInt(s[pos]) : 9;
let total = 0;
// try every valid digit, skipping d once the number has started
for (let digit = 0; digit <= limit; digit++) {
const willStart = (started || digit !== 0) ? 1 : 0;
if (willStart && digit === d)
continue;
const newTight = (tight && digit === limit) ? 1 : 0;
total += dp[newTight][willStart];
}
newDp[tight][started] = total;
}
}
dp = newDp;
}
return dp[1][0];
}
// Driver Code
const n = 25, d = 3;
console.log(countWithout(n, d));
Output
22