C++で文法列のN行目・K番目の記号を再帰的に求める方法
この問題では、最初の行が 0 から始まる特別な数列を扱います。それ以降の各行では、直前の行を参照し、0 を「01」に、1 を「10」に置き換えることで新しい行を生成していきます。
N 行とインデックス K が与えられたとき、N 行目の K 番目の記号を求めるのが目的です(※ K は 1 から始まるインデックスです)。
たとえば N = 4、K = 5 の場合、出力は 1 になります。その理由は以下の通りです。
- 行 1: 0
- 行 2: 01
- 行 3: 0110
- 行 4: 01101001
行 4 の 5 番目の文字を数えると「1」であることが確認できます。
解法のアプローチ
この問題は、行が生成される規則性に注目すると再帰的に解くことができます。各行は前の行の各記号が必ず 2 文字に展開されるため、N 行目の位置 K は、N−1 行目の位置 ⌈K/2⌉ に対応します。
具体的には次の手順で解きます。
- メソッド名を
kthGrammarとし、引数として N と K を受け取ります。 - N が 1 の場合は、行全体が「0」なので 0 を返す。
- K が偶数の場合、その記号は親の記号が反転したものです。つまり
kthGrammar(N − 1, K / 2)が 0 なら 1 を返し、そうでなければ 0 を返す。 - K が奇数の場合、その記号は親の記号と同じなので、
kthGrammar(N − 1, (K + 1) / 2)の結果をそのまま返す。
C++での実装例
以下のコードで実際の実装を確認してみましょう。
#include <bits/stdc++.h>
using namespace std;
class Solution {
public:
int kthGrammar(int N, int K) {
if(N == 1) return 0;
if(K % 2 == 0){
return kthGrammar(N - 1, K / 2) == 0 ? 1 : 0;
}else{
return kthGrammar(N - 1, (K + 1) / 2);
}
}
};
main(){
Solution ob;
cout << (ob.kthGrammar(4, 5));
}入力
4 5
出力
1
計算量について
この再帰解法は、各呼び出しごとに行数 N が 1 ずつ減り、探索範囲も半分になるため、時間計算量は O(N)、再帰による空間計算量も O(N) となります。行を実際に構築して保存する必要がないため、N が大きい場合でも効率的に動作する点が大きなメリットです。
-
C++で文字列として表現された二分木のk番目のレベルにあるノードの積を求める方法
はじめに 文字列形式で表現された二分木が与えられたとき、k番目のレベルに存在するノードの値の積を求めるのが本記事の目的です。二分木の各ノードは、データ部分・左部分木を指すポインタ・右部分木を指すポインタの3つの要素で構成されています。 二分木のレベルは0から始まり、任意の正の整数nまで続きます。ここでは、レベル「k」が与えられ、そのレベルにあるノードの値の積をプログラムで計算します。 例えば、次のような二分木に対してk=2が与えられた場合を考えてみましょう。 レベル2のノードは − 40、50、60 です。 積 = 40 × 50 × 60 = 120,000 入力例1 (1(2(3()()
-
C++における「&」記号の使い方とは?ビットAND演算子とアドレス演算子を徹底解説
C++において「&」記号は演算子として使用されます。その用途は主に2つあり、1つはビット単位のAND演算子、もう1つは変数のアドレスを取得するアドレス演算子です。それぞれの役割と具体的な使い方を、サンプルコードとともに見ていきましょう。 ビット単位のAND(Bitwise AND) ビット単位のAND演算子(&)は、第1オペランドの各ビットと第2オペランドの対応するビットを比較します。両方のビットが1の場合のみ結果のビットが1に設定され、それ以外の場合は0になります。なお、この演算子の両オペランドは整数型である必要があります。 コード例 #include <iostream>