Pythonで文字列の相異なる部分列の数を数えるプログラム
文字列 s が与えられたとき、その文字列から作ることができる相異なる部分列(サブシーケンス)の総数を求める問題です。答えが非常に大きくなる可能性があるため、結果は 109 + 7 で割った余りとして返します。
たとえば、入力が s = "bab" の場合、出力は 6 になります。これは "a"、"b"、"ba"、"ab"、"bb"、"bab" の 6 つの異なる部分列が存在するためです。
解法のアプローチ
この問題は動的計画法(DP)を用いて効率的に解くことができます。手順は以下の通りです。
- dp := 文字列 s と同じ長さの配列を作成し、すべての要素を 0 で初期化する
- m := 109 + 7(剰余を取るための定数)
- 文字列 s の各インデックス i と文字 char について、次の処理を行う
- ind := インデックス i より前の範囲で、char と同じ文字が最後に現れた位置(rfind で検索)
- ind が -1 の場合(同じ文字がまだ一度も現れていない場合):dp[i] := 1 + (dp[0]〜dp[i-1] の合計) mod m
それ以外の場合:dp[i] := (dp[ind]〜dp[i-1] の合計) mod m
- 最後に、dp の全要素の合計を m で割った余りを返す
考え方のポイント
各位置 i における dp[i] は、「i 番目の文字で終わる新しく追加される部分列」の数を表しています。同じ文字がすでに出現している場合は、過去に数えた部分列との重複を避けるため、前回の出現位置以降の合計だけを加算するのがポイントです。これにより、全体の部分列の重複カウントを防ぎながら正確な数え上げが可能になります。
実装例
理解を深めるために、以下の Python 実装を見てみましょう。
def solve(s):
dp, m = [0] * len(s), 10**9 + 7
for i, char in enumerate(s):
ind = s.rfind(char, 0, i)
dp[i] = 1 + sum(dp[:i]) % m if ind == -1 else sum(dp[ind:i]) % m
return sum(dp) % m
s = "baby"
s = "baby"
入力
"baby"
出力
6
-
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