Pythonで長さkの増加部分列の個数を動的計画法で求める方法
数値のリスト nums と整数 k が与えられたとき、「厳密に増加する」サイズ k の部分列(サブシーケンス)が何個存在するかを求めます。答えが非常に大きくなる可能性があるため、10^9 + 7 で割った余りを返します。
たとえば、nums = [2, 3, 4, 1]、k = 2 の場合、出力は 3 になります。これは、サイズ 2 の増加部分列として [2, 3]、[3, 4]、[2, 4] の 3 つが存在するためです。
解法のアプローチ
この問題は動的計画法(DP)を用いて効率的に解くことができます。dp[j] は「インデックス j の要素を末尾とする、現在の長さの増加部分列の個数」を表し、各イテレーションで部分列を 1 要素ずつ伸ばしていきます。手順は以下の通りです。
- m := 10^9 + 7(剰余演算用の定数)
- dp := nums と同じサイズのリストを作成し、すべて 1 で初期化
- 次の処理を k 回繰り返す:
- j を dp のサイズ - 1 から 0 まで 1 ずつ減らしながら繰り返す:
- dp[j] := 0
- i を 0 から j - 1 まで繰り返す:
- nums[i] < nums[j] の場合、dp[j] := dp[j] + dp[i]
- j を dp のサイズ - 1 から 0 まで 1 ずつ減らしながら繰り返す:
- dp の全要素の合計を m で割った余りを返す
Pythonでの実装例
理解を深めるために、以下の実装を見てみましょう。
class Solution: def solve(self, nums, k): m = 10 ** 9 + 7 dp = [1] * len(nums) for _ in range(k - 1): for j in range(len(dp) - 1, -1, -1): dp[j] = 0 for i in range(j): if nums[i] < nums[j]: dp[j] += dp[i] return sum(dp) % m ob = Solution() nums = [2, 3, 4, 1] k = 2 print(ob.solve(nums, k))
入力
[2, 3, 4, 1], 2
出力
3
計算量について
このアルゴリズムの時間計算量は O(k × n²)(n はリストの長さ)、空間計算量は O(n) です。n や k が大きい場合は、Binary Indexed Tree(BIT)などのデータ構造を使うことでさらに高速化することも可能です。
-
Pythonで二分探索木(BST)の指定範囲内にあるノード数を求める方法
問題の概要 二分探索木(BST)が与えられ、さらに左側の境界値 l と右側の境界値 r が指定されます。このとき、木に含まれるすべてのノードの中で、値が l 以上 r 以下の範囲内にあるノードの個数を求めるのが目的です。 例えば、次のような木が与えられたとします。 このとき l = 7、r = 13 とすると、範囲内に含まれるノードは 8、10、12 の3つなので、出力は 3 になります。 アルゴリズムの考え方 スタックを使った反復的な深さ優先探索(DFS)で木をたどります。重要なポイントは、二分探索木の性質を活かして枝刈り(pruning)を行うことです。値が境界より小さいノードの左側の
-
Pythonでリスト内の最大値を見つける方法|sort()とmax()の2つのアプローチ
この記事では、リストの中から最大の数値を見つけるための解決策とアプローチについて詳しく解説します。問題の概要数値のリストが与えられたとき、その中から最大の要素を見つけ出す必要があります。Pythonでは、主に以下の2つの方法でこれを実現できます。ソート(並べ替え)を利用する方法組み込み関数 max() を利用する方法アプローチ1:sort() 関数を使う方法リストを sort() メソッドで昇順に並べ替えると、リストの最後の要素(インデックス -1)が必ず最大値になります。サンプルコードlist1 = [18, 65, 78, 89, 90] list1.sort() # メイン処理 prin