C++で無限整数列のN番目の桁を効率的に求める方法
問題の概要
「12345678910111213...」のように無限に続く整数列を考えます。この数列のn番目の桁がどの数字になるかを求めるのが本記事の課題です。例えば、入力が11の場合、数列を並べると「123456789101112」となり、11番目の桁は「0」であるため、出力は0になります。
アルゴリズムの考え方
すべての数字を実際に並べて数えるのは非効率です。そこで、桁数ごとの数字の個数を利用し、n番目の桁がどの数に属するかを効率的に特定します。1桁の数(1〜9)は9個で合計9桁、2桁の数(10〜99)は90個で合計180桁、3桁の数(100〜999)は900個で合計2700桁というように、各桁数の寄与を順番に差し引いていくのがポイントです。
具体的な手順は以下の通りです。
- len(現在の桁数)を1、cnt(その桁数の数の個数)を9、start(その桁数の最初の数)を1として初期化する
- n > len * cnt の間、以下を繰り返す:
- n から len * cnt を引く
- cnt を10倍、start を10倍する
- len を1増やす
- start に (n - 1) / len を加算し、対象となる数を特定する
- start を文字列 s に変換する
- s[(n - 1) mod len] を返す(その数の中で何桁目かを特定)
C++での実装例
理解を深めるために、以下の実装例を見てみましょう。
#include <bits/stdc++.h>
using namespace std;
typedef long long int lli;
class Solution {
public:
int findNthDigit(int n) {
lli len = 1;
lli cnt = 9;
lli start = 1;
while(n > len * cnt){
n -= len * cnt;
cnt *= 10;
start *= 10;
len++;
}
start += (n - 1) / len;
string s = to_string(start);
return s[(n - 1) % len] - '0';
}
};
main(){
Solution ob;
cout << (ob.findNthDigit(11));
}入力
11
出力
0
処理の流れ(入力が11の場合)
初期状態は len=1、cnt=9、start=1 です。まず n=11 は len*cnt=9 より大きいため、n から9を引いて n=2 となり、cnt=90、start=10、len=2 に更新されます。次に n=2 は len*cnt=180 より小さいためループを抜けます。start に (2-1)/2=0 を加算しても start=10 のままです。最後に、文字列「10」の (2-1)%2=1 番目の文字は「0」なので、答えは0となります。
このアルゴリズムの計算量は桁数に対して対数時間 O(log n) であり、非常に大きな n に対しても高速に動作するのが特徴です。
-
C++ STLのiswdigit()関数とは?使い方をわかりやすく解説
C++ STLにおけるiswdigit()関数は、指定されたワイド文字が10進数字(decimal digit)であるかどうかを判定するための組み込み関数です。この関数は、C/C++の <cwctype> ヘッダーファイル内に定義されています。10進数字とは?10進数字とは、0から始まる数値を表す文字のことで、具体的には「0、1、2、3、4、5、6、7、8、9」の10種類を指します。iswdigit()関数の構文int iswdigit(wint_t c);パラメータ: c は判定対象となるワイド文字で、wint_t 型にキャストして渡すか、WEOF を指定します。wint_t は
-
C++で数字kを含む、またはkで割り切れるn番目の数を求める方法
問題の概要 2つの正整数 n と k が与えられ、「数字 k を含む数、または k で割り切れる数」のうち n 番目の数を求めます。ただし、k の範囲は 2〜9 とします。 例えば n = 15、k = 3 の場合、答えは 33 です。条件を満たす数は [3, 6, 9, 12, 13, 15, 18, 21, 23, 24, 27, 30, 31, 33] と並んでおり、これらはそれぞれ「3 という数字を含む」か「3 で割り切れる」数です。このうち n 番目にあたるのが 33 なので、出力は 33 となります。 解法のアプローチ この問題は、シンプルなシミュレーション(全探索)で解くことがで