Pythonで1からnまでの2進数表現を連結した値を求めるプログラム
問題概要
整数 n が与えられたとき、1 から n までの各数値を2進数表現に変換し、それらを順番に連結してできる2進文字列の10進数としての値を求めます。答えが非常に大きくなる場合は、109 + 7 で割った余りを返します。
例えば、n = 4 の場合を考えてみましょう。1 から 4 までの2進数表現を順に連結すると、次のようになります。
"1" + "10" + "11" + "100" = "110111000"
この2進数「110111000」を10進数に変換すると 220 になります。これが求める答えです。
解法のアプローチ
この問題は、以下の手順で解くことができます。
- 答え ans を 1 で初期化します(最初の「1」に相当)。
- 剰余を取るための定数 m を 109 + 7 とします。
- i を 2 から n まで順に処理します。
- ans を i のビット長分だけ左シフトします。
- ans に i を加算し、m で割った余りを取ります。
- 最終的な ans を返します。
ポイント解説
左シフト演算(ans << i.bit_length())を行うと、ans の2進表現の末尾に i のビット長分だけ 0 が追加されます。そこに i を加算することで、i の2進表現がそのまま末尾に連結されたことになります。つまり、文字列を実際に結合しなくても、ビット演算だけで高速に連結処理を実現できるのがこの手法の魅力です。
実装例(Python)
以下がPythonでの実装例です。
def solve(n):
ans = 1
m = (10**9+7)
for i in range(2, n+1):
ans = ans << i.bit_length()
ans = (ans+i) % m
return ans
n = 4
print(solve(n))
入力
4
出力
220
計算量について
ループは 2 から n まで合計 n 回程度実行されるため、時間計算量は O(n) となります。また、使用する変数は ans と m のみなので、空間計算量は O(1) です。大きな n に対しても効率的に動作する実装といえます。
-
Pythonで二分木内の長さkの一意なパスを数えるプログラム
問題概要 一意な値を持つ二分木と整数 k が与えられます。このとき、木の中に存在する「長さ k の一意なパス」の総数を求めます。パスは親ノードから子ノードへ向かう方向でも、子ノードから親ノードへ向かう方向でも構いません。また、あるノードが片方のパスにのみ含まれる場合、その2つのパスは互いに異なるものとして扱います。 入力例と出力例 たとえば、次のような二分木が与えられたとします。 k = 3 の場合、出力は 4 になります。該当するパスは次の4本です。 [12, 8, 3] [12, 8, 10] [8, 12, 15] [3, 8, 10] 解き方:深さ優先探索(DFS)によるアプ
-
連続する「1」を含まないバイナリ文字列の数を数えるPythonプログラム
この記事では、「連続する1が存在しないバイナリ文字列の総数を求める」という問題の解き方について、Pythonでの実装例を交えながら詳しく解説します。 問題文 問題: 正の整数 N が与えられます。このとき、長さ N のバイナリ文字列(0と1のみで構成される文字列)のうち、連続する「1」が一切含まれないものの総数を求めてください。 例えば N = 3 の場合、有効な文字列は「000」「001」「010」「100」「101」の5つとなり、「011」「110」「111」は連続する1を含むため除外されます。 アプローチ:動的計画法 この問題は動的計画法(DP)を使うことで効率的に解けます。各桁の状態を