Python
 Computer >> コンピューター >  >> プログラミング >> Python

Pythonで解く「Kプレフィックス」問題:累積和を使って最大インデックスを効率的に求める方法


問題の概要

数値のリスト nums と整数 k が与えられたとき、「nums[0] + nums[1] + ... + nums[i] ≤ k」という条件を満たす最大のインデックス i を求めるのがこの問題です。条件を満たす i がひとつも存在しない場合は、-1 を返します。

たとえば、nums = [4, -7, 5, 2, 6]k = 5 という入力の場合、答えは 3 になります。これは、先頭から nums[3] までを足すと 4 + (-7) + 5 + 2 = 4 となり、k 以下だからです。しかし最後の要素まで加えると合計が k を超えてしまうため、有効な最大インデックスは 3 ということになります。

解法のアプローチ:累積和(Prefix Sum)を活用する

この問題は「累積和」を使うことでシンプルに解決できます。手順は以下の通りです。

  • ステップ1: インデックス 1 から順に、各要素へ直前の要素を加算していき、元のリストをそのまま累積和の配列に変換します。
    • nums[i] := nums[i] + nums[i-1]
  • ステップ2: 変換後の配列を末尾から先頭へ向かって走査します。nums[i] ≤ k を満たす最初のインデックスが見つかった時点で、それが答えなので即座に返します。
  • ステップ3: 最後まで条件を満たす要素が存在しなければ、-1 を返します。

リストには負の数も含まれる可能性があるため、累積和は単調増加とは限りません。そのため二分探索などは使えず、末尾から順に確認することで確実に「最大のインデックス」を見つけられるのがこの手法のポイントです。

実装例

以下は Python による具体的な実装です。

class Solution:
    def solve(self, nums, k):
        # 累積和を作成
        for i in range(1, len(nums)):
            nums[i] += nums[i-1]
        # 後ろから条件を満たす最大のインデックスを探す
        for i in range(len(nums)-1, -1, -1):
            if nums[i] <= k:
                return i
        return -1

ob = Solution()
nums = [4, -7, 5, 2, 6]
k = 5
print(ob.solve(nums, k))

入力

[4, -7, 5, 2, 6], 5

出力

3

計算量の評価

累積和の構築に O(n)、後方からの走査にも O(n) しかかからないため、全体の時間計算量は O(n) です。また、入力リストをそのまま再利用しているため、追加のメモリ消費は O(1) で済みます。シンプルながら非常に効率的な解法と言えます。


  1. Python bisectモジュール入門:二分探索でリストを常にソート済みに保つ方法

    長いリストに対して、要素を挿入するたびにソート処理を実行すると、プロセッサへの負荷が大きく、時間もかかってしまいます。Pythonのbisectモジュールを使えば、二分探索(バイセクション)アルゴリズムによって、要素を挿入した後もリストが自動的にソートされた状態を維持できます。このモジュールには、主に以下の関数が用意されています。bisect_left()指定した要素を挿入すべき位置(挿入ポイント)を、リストのソート順序を維持できるように検索します。同じ値の要素がすでにリスト内に存在する場合は、その既存要素の左側(手前)が挿入ポイントとして返されます。戻り値は list.insert() の第

  2. Pythonで数値を文字列としてフォーマットする方法を解説

    Pythonでは、文字列のformatメソッドを使うことで、浮動小数点数を固定幅で整形したり、整数を見やすい形式に整えたりすることができます。この記事では、数値フォーマットの基本的な使い方を実例とともに紹介します。 浮動小数点数を固定幅でフォーマットする 書式指定子 {:10.4f} を使うと、全体の幅を10桁、小数点以下を4桁に揃えて表示できます。 コード例 nums = [0.555555555555, 1, 12.0542184, 5589.6654753] for x in nums: print({:10.4f}.format(x)) 出力結果 0.5556 1.0000 1