Pythonで合計がターゲットと等しい重複しない部分配列の最大数を求めるプログラム
問題の概要
配列 nums と値 target が与えられたとします。このとき、各部分配列(サブ配列)の要素の合計が target と等しくなるような、空でない重複しない(オーバーラップしない)部分配列の最大数を求める必要があります。
例として、nums = [3,2,4,5,2,1,5]、target = 6 の場合を考えてみましょう。この場合の出力は 2 になります。これは、合計が 6 となる部分配列 [2,4] と [1,5] の2つが存在するためです。
解法のアプローチ
この問題は、累積和(プレフィックスサム)とセットを組み合わせることで、線形時間で効率的に解くことができます。手順は以下の通りです。
t:= 要素 0 のみを含む新しいセットtemp:= 0(現在位置までの累積和)ans:= 0(答えのカウント)numsの各要素iについて以下を繰り返す:temp := temp + iprev := temp - targetprevがtに含まれる場合:ans := ans + 1tを要素tempのみを含む新しいセットでリセット
- それ以外の場合:
tempをtに追加
ansを返す
なぜこの方法が機能するのか
ある位置での累積和から target を引いた値が、前回見つけた部分配列の終了位置以降に記録した累積和のセットの中に存在すれば、「その位置で終わる・合計が target に等しい新しい部分配列」が存在することを意味します。部分配列を見つけた時点でセットをリセットすることで、すでに採用した部分配列との重なりを防ぎ、常に非重複の条件を満たせるようになっています。また、最初に 0 をセットに入れておくことで、配列の先頭から始まる部分配列も正しく検出できます。
実装例
以下にPythonでの実装を示します。
def solve(nums, target):
t = set([0])
temp = 0
ans = 0
for i in nums:
temp += i
prev = temp - target
if prev in t:
ans += 1
t = set([temp])
else:
t.add(temp)
return ans
nums = [3,2,4,5,2,1,5]
target = 6
print(solve(nums, target))
入力
nums = [3,2,4,5,2,1,5], target = 6
出力
2
計算量の評価
配列を一度だけ走査し、セットへの挿入・参照は平均 O(1) で行えるため、時間計算量は O(n)、空間計算量も O(n) となります。非常に大きな入力に対しても高速に動作する効率的なアルゴリズムです。
-
Pythonで同じラベルを持つサブツリー内のノード数を求めるプログラム
ここでは、n個のノードからなる根付きの一般木を考えます。ノードには0からn-1までの番号が振られており、各ノードには小文字の英字ラベルが割り当てられています。ラベルは配列labelsとして与えられ(labels[i]がi番目のノードのラベル)、木は辺リストで表現されます。各辺eは[u, v]という形式で、uが親、vが子であることを意味します。 求めたいのは、サイズnの配列Aです。A[i]には「i番目のノードと同じラベルを持つ、そのサブツリー内のノードの総数」を格納します。 例えば、入力が次のような場合を考えてみましょう。 n = 5、label = ccaca のとき、出力は [3, 2,
-
Pythonで最大の成功確率を持つパスを見つけるプログラムの実装方法
問題の概要 n 個のノード(ノードには 0 から順に番号が振られています)からなる無向重み付きグラフを考えます。このグラフは辺リスト(edge list)として入力され、各辺 e には「その辺を通過する際の成功確率」probability[e] が割り当てられています。さらに、開始ノード(start)と終了ノード(end)も与えられます。 求めたいのは、start から end へ移動するときに成功確率が最大となる経路であり、答えとしてその成功確率を返します。経路がひとつも存在しない場合は 0 を返してください。 たとえば、次のような入力が与えられたとします。 この場合の出力は 0.25