From 127e9da59bfa85dfa028d2f9aa0c626f9d902ef9 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 8 Sep 2026 04:59:40 +0000 Subject: [PATCH] feat: add solution for 3870. Count Commas in Range --- .../analysis_daily_20260908.md | 74 +++++++++++++++++++ .../3870-count-commas-in-range/problem.md | 56 ++++++++++++++ .../solution_daily_20260908.go | 25 +++++++ .../solution_daily_20260908_test.go | 47 ++++++++++++ 4 files changed, 202 insertions(+) create mode 100644 problems/3870-count-commas-in-range/analysis_daily_20260908.md create mode 100644 problems/3870-count-commas-in-range/problem.md create mode 100644 problems/3870-count-commas-in-range/solution_daily_20260908.go create mode 100644 problems/3870-count-commas-in-range/solution_daily_20260908_test.go diff --git a/problems/3870-count-commas-in-range/analysis_daily_20260908.md b/problems/3870-count-commas-in-range/analysis_daily_20260908.md new file mode 100644 index 0000000..8dda4a1 --- /dev/null +++ b/problems/3870-count-commas-in-range/analysis_daily_20260908.md @@ -0,0 +1,74 @@ +# 3870. Count Commas in Range + +[LeetCode Link](https://leetcode.com/problems/count-commas-in-range/) + +Difficulty: Easy +Topics: Math +Acceptance Rate: 73.5% + +## Hints + +### Hint 1 + +The constraints are small enough that a straightforward loop over every number in `[1, n]` will pass. But before you write that loop, ask a sharper question: for a *single* number, what determines how many commas it gets? It has nothing to do with the number's value — only with how many digits it has. Once you see that, you're doing counting, not simulation. + +### Hint 2 + +A number with `d` digits gets `(d-1)/3` commas (integer division): 3 digits → 0, 4 through 6 digits → 1, 7 through 9 digits → 2. So the total answer is a sum of `(d-1)/3` over all numbers from 1 to `n`. Instead of computing that per number, try grouping numbers by digit count — how many numbers in `[1, n]` have exactly 4 digits? Exactly 5? You can count each block in O(1). + +### Hint 3 + +There's an even cleaner reframing that avoids grouping entirely. Flip the sum around: instead of asking "how many commas does each number have," ask "how many numbers does each comma *position* apply to." + +`(d-1)/3` is exactly the count of integers `k >= 1` with `3k <= d-1`, i.e. `d >= 3k+1`, i.e. the number is at least `10^(3k)`. So the k-th comma slot (thousands, millions, billions, ...) contributes one comma for every number `>= 10^(3k)`. + +That turns the whole problem into a tiny sum: + +``` +answer = sum over k >= 1 of max(0, n - 10^(3k) + 1) +``` + +## Approach + +The key move is exchanging the order of summation. Naively we sum over numbers and, for each, count its commas. Instead we sum over *comma slots* and, for each slot, count how many numbers use it. + +**Step 1 — commas per number.** Standard formatting inserts a comma after every three digits from the right. A `d`-digit number therefore has separators at `d-3`, `d-6`, ... as long as those positions leave at least one digit to the left. That count is `(d-1)/3` with integer division. + +**Step 2 — reindex the sum.** Notice that `(d-1)/3 = #{k >= 1 : 3k <= d-1}`. The condition `d >= 3k+1` says the number has at least `3k+1` digits, which is the same as saying the number is `>= 10^(3k)`. So: + +``` +total = sum_{x=1..n} #{k : x >= 10^(3k)} + = sum_{k >= 1} #{x in [1, n] : x >= 10^(3k)} + = sum_{k >= 1} max(0, n - 10^(3k) + 1) +``` + +**Step 3 — evaluate.** Walk `p = 1000, 1000000, 1000000000, ...` and while `p <= n`, add `n - p + 1`. Once `p > n` every later term is zero, so we stop. That's at most a handful of iterations. + +**Worked example, `n = 1002`:** + +- `p = 1000`: `1000 <= 1002`, add `1002 - 1000 + 1 = 3`. (These are 1000, 1001, 1002 — each written `"1,00x"`.) +- `p = 1000000`: exceeds `n`, stop. +- Answer: **3**. ✓ + +**Worked example, `n = 1000000`:** + +- `p = 1000`: add `1000000 - 1000 + 1 = 999001`. +- `p = 1000000`: add `1000000 - 1000000 + 1 = 1`. (That single number is `"1,000,000"`, whose *second* comma this term accounts for.) +- Answer: **999002**. + +Each term isolates one comma column, which is why a number with two commas correctly gets counted twice — once by the thousands term and once by the millions term. + +An O(n) loop over `[1, n]` accumulating `(digits-1)/3` is perfectly acceptable for `n <= 10^5` and is a fine thing to write first. The closed form is worth understanding anyway: it's the same "count contributions instead of simulating" trick that shows up in much harder digit-DP and combinatorics problems, and here it's visible without much machinery. + +## Complexity Analysis + +Time Complexity: O(log n) — one iteration per group of three digits in `n`, at most 6-7 iterations for any 64-bit input. +Space Complexity: O(1) — a couple of integer accumulators. + +## Edge Cases + +- **`n < 1000` (e.g. `n = 1`, `n = 998`, `n = 999`)** — the loop body never executes and the answer is `0`. This is example 2, and it's the case a solution that assumes at least one comma exists would get wrong. +- **`n = 1000` exactly** — the boundary where the first comma appears. The condition must be `p <= n`, not `p < n`, and the term is `n - p + 1 = 1`, not `n - p = 0`. Off-by-one here is the most likely bug. +- **`n = 999`** — the value just below the boundary, the natural partner test to `n = 1000`. +- **Numbers with more than one comma (`n >= 10^6`)** — outside the stated constraint of `n <= 10^5`, but the reindexed sum handles them for free, and testing one confirms the multi-comma logic. A solution that only ever adds the thousands term would silently pass the official constraints and still be wrong in general. +- **Overflow when advancing `p`** — multiplying `p` by 1000 unconditionally can wrap around for large `n`. Guard the multiplication (check `p > n/1000` before scaling) so the loop terminates cleanly rather than wrapping to a negative value. diff --git a/problems/3870-count-commas-in-range/problem.md b/problems/3870-count-commas-in-range/problem.md new file mode 100644 index 0000000..7b1dd79 --- /dev/null +++ b/problems/3870-count-commas-in-range/problem.md @@ -0,0 +1,56 @@ +--- +number: "3870" +frontend_id: "3870" +title: "Count Commas in Range" +slug: "count-commas-in-range" +difficulty: "Easy" +topics: + - "Math" +acceptance_rate: 7346.8 +is_premium: false +created_at: "2026-09-08T04:57:47.921771+00:00" +fetched_at: "2026-09-08T04:57:47.921771+00:00" +link: "https://leetcode.com/problems/count-commas-in-range/" +date: "2026-09-08" +--- + +# 3870. Count Commas in Range + +You are given an integer `n`. + +Return the **total** number of commas used when writing all integers from `[1, n]` (inclusive) in **standard** number formatting. + +In **standard** formatting: + + * A comma is inserted after **every three** digits from the right. + * Numbers with **fewer** than 4 digits contain no commas. + + + + + +**Example 1:** + +**Input:** n = 1002 + +**Output:** 3 + +**Explanation:** + +The numbers `"1,000"`, `"1,001"`, and `"1,002"` each contain one comma, giving a total of 3. + +**Example 2:** + +**Input:** n = 998 + +**Output:** 0 + +**Explanation:** + +All numbers from 1 to 998 have fewer than four digits. Therefore, no commas are used. + + + +**Constraints:** + + * `1 <= n <= 105` diff --git a/problems/3870-count-commas-in-range/solution_daily_20260908.go b/problems/3870-count-commas-in-range/solution_daily_20260908.go new file mode 100644 index 0000000..2bfce59 --- /dev/null +++ b/problems/3870-count-commas-in-range/solution_daily_20260908.go @@ -0,0 +1,25 @@ +package main + +// 3870. Count Commas in Range +// +// A d-digit number carries (d-1)/3 commas, and (d-1)/3 is exactly the number of +// k >= 1 with d >= 3k+1 -- that is, the number of powers 10^(3k) it reaches. +// So instead of summing commas per number, sum over comma columns: the k-th +// column (thousands, millions, ...) contributes one comma for every value in +// [1, n] that is at least 10^(3k), i.e. max(0, n-10^(3k)+1) of them. +// +// answer = sum over k >= 1 of max(0, n - 10^(3k) + 1) +// +// Runs in O(log n) with O(1) extra space. +func countCommas(n int) int { + total := 0 + for p := 1000; p <= n; { + total += n - p + 1 + // Stop before p*1000 would overflow; any such p already exceeds n. + if p > n/1000 { + break + } + p *= 1000 + } + return total +} diff --git a/problems/3870-count-commas-in-range/solution_daily_20260908_test.go b/problems/3870-count-commas-in-range/solution_daily_20260908_test.go new file mode 100644 index 0000000..ad37761 --- /dev/null +++ b/problems/3870-count-commas-in-range/solution_daily_20260908_test.go @@ -0,0 +1,47 @@ +package main + +import ( + "strconv" + "testing" +) + +func TestCountCommas(t *testing.T) { + tests := []struct { + name string + n int + expected int + }{ + {"example 1: n = 1002, the three numbers 1,000 through 1,002", 1002, 3}, + {"example 2: n = 998, every number has fewer than four digits", 998, 0}, + {"edge case: n = 1, smallest allowed input", 1, 0}, + {"edge case: n = 999, just below the first comma", 999, 0}, + {"edge case: n = 1000, exactly at the first comma", 1000, 1}, + {"edge case: n = 1001, two commas total", 1001, 2}, + {"edge case: n = 9999, every four-digit number counted", 9999, 9000}, + {"edge case: n = 100000, upper bound of the constraints", 100000, 99001}, + {"edge case: n = 1000000, first number with two commas", 1000000, 999002}, + {"edge case: n = 1000001, past the second comma boundary", 1000001, 999004}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := countCommas(tt.n); got != tt.expected { + t.Errorf("countCommas(%d) = %v, want %v", tt.n, got, tt.expected) + } + }) + } +} + +// TestCountCommasMatchesBruteForce cross-checks the closed form against a +// reference that formats each number and counts separators directly. The +// running total is carried forward so every prefix [1, n] is verified in a +// single linear sweep. +func TestCountCommasMatchesBruteForce(t *testing.T) { + want := 0 + for n := 1; n <= 200000; n++ { + want += (len(strconv.Itoa(n)) - 1) / 3 + if got := countCommas(n); got != want { + t.Fatalf("countCommas(%d) = %v, want %v", n, got, want) + } + } +}