C++
 Computer >> コンピューター >  >> プログラミング >> C++

C++で0からnまでの整数に含まれる「1」の出現回数を効率的に数える方法

問題概要

非負整数 n が与えられたとき、0 以上 n 以下のすべての整数に登場する数字「1」の合計出現回数を求める問題です。

たとえば入力が 15 の場合を考えてみましょう。「1」を含む数は [1, 10, 11, 12, 13, 14, 15] であり、これらに含まれる「1」は合計 8 個 です(11 には「1」が2個含まれる点に注意してください)。したがって出力は 8 となります。

解法のアプローチ

すべての数を1つずつ調べる方法では計算量が膨大になるため、ここでは桁ごとに「1」の出現回数を数える効率的な手法を採用します。1の位・10の位・100の位…と各桁の位置について、その桁に「1」が現れる回数を数学的に導出していきます。

具体的な手順は次のとおりです。

  • ret := 0 として結果を初期化します。

  • i := 1(1の位)から開始し、i <= n の間、i = i * 10 として桁をずらしながら繰り返します。

    • a := n / i(注目桁より上位の部分)、b := n mod i(注目桁より下位の部分)、x := a mod 10(現在注目している桁の数字)を求めます。

    • x == 1 の場合:
      ret = ret + (a / 10) * i + (b + 1)

    • x == 0 の場合:
      ret = ret + (a / 10) * i

    • それ以外(x >= 2)の場合:
      ret = ret + (a / 10 + 1) * i

  • 最後に ret を返します。

なぜこの式が成り立つのか

ある桁に「1」が現れる回数は、「その桁より上位の部分」と「下位の部分」の組み合わせによって決まります。

  • 注目桁が 0 のとき: 上位部分が 0 ~ (a/10 − 1) の範囲でのみ「1」が出現するため、(a / 10) * i 回となります。
  • 注目桁が 1 のとき: 上位部分が a/10 の場合でも、下位部分が 0 ~ b の範囲で追加的に「1」が出現するため、(a / 10) * i + (b + 1) 回となります。
  • 注目桁が 2 以上のとき: 上位部分が 0 ~ a/10 のすべての範囲で「1」が出現するため、(a / 10 + 1) * i 回となります。

C++による実装例

それでは、実際のコードを見て理解を深めましょう。

#include <bits/stdc++.h>
using namespace std;
class Solution {
   public:
   int countDigitOne(int n) {
      int ret = 0;
      for(long long int i = 1; i <= n; i*= (long long int)10){
         int a = n / i;
         int b = n % i;
         int x = a % 10;
         if(x ==1){
            ret += (a / 10) * i + (b + 1);
         }
         else if(x == 0){
            ret += (a / 10) * i;
         } else {
            ret += (a / 10 + 1) *i;
         }
      }
      return ret;
   }
};
main(){
   Solution ob;
   cout << (ob.countDigitOne(15));
}

入力

15

出力

8

計算量について

ループは n の桁数分だけしか回転しないため、時間計算量は O(log₁₀ n)、空間計算量は O(1) です。n が非常に大きな値であっても高速に動作するのが、このアルゴリズムの大きな利点です。

  1. C++で質素数(Frugal Number)を判定する方法【サンプルコード付き】

    この記事では、正の整数 N が与えられたときに、その数が質素数(Frugal Number)であるかどうかを判定するプログラムを C++ で作成する方法を解説します。 質素数とは? 質素数(FRUGAL NUMBER)とは、その数自身の桁数が、素因数分解による表現の桁数よりも厳密に大きい数のことです。 例:625 の場合 625 を素因数分解すると 54 となります。 625 自身の桁数:3 桁 54 の表現の桁数:2 桁 3 は 2 よりも厳密に大きいため、625 は質素数です。 最初のいくつかの質素数:125、128、243、256、343、512、625 など 問題を理解するための具

  2. C++で五胞体数(ペンタトープ数)を求める方法

    五胞体数とは? 五胞体数(ペンタトープ数)は、パスカルの三角形の第5の対角線上に現れる数列として知られています。この数列を定義するには、パスカルの三角形に少なくとも5つの数が必要となるため、数列の最初の数はパスカルの三角形の第4行である 1 4 6 4 1 から始まります。 本チュートリアルでは、n番目の五胞体数を求める方法を解説します。まずは具体的な例を見てみましょう。 入力 : 1出力 : 1入力 : 4出力 : 35 以下の図から出力を確認できます。 この問題は数列に関するものなので、解法ではまず数列のパターンを見つけることから始めます。 解法のアプローチ このプログラムでは、数列の