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

Pythonで「良い部分配列」の最大スコアを求めるアルゴリズムと実装

問題の概要

整数配列 nums とインデックス k が与えられます。部分配列 (i, j) のスコアは、次のように定義されます。

score(i, j) = min(nums[i..j]) × (j − i + 1)

つまり「部分配列内の最小値 × 部分配列の長さ」です。ここで、i ≤ k ≤ j を満たす部分配列を「良い部分配列(good subarray)」と呼びます。この記事の目的は、良い部分配列の中から最大のスコアを見つけることです。

入力例

nums = [2,5,4,8,5,6]、k = 3 の場合を考えてみましょう。最適な部分配列は (1, 5) で、nums[1..5] の最小値は 4 です。したがって、スコアは 4 × (5 − 1 + 1) = 20 となります。

解法の考え方

この問題は、インデックス k を中心に領域を左右へ拡張していく貪欲法を使えば、O(n) の計算量で効率的に解けます。基本的なアイデアは次のとおりです。

  • まず k の位置だけを範囲とし、そこから左右のポインタ i と j を広げていきます。
  • 現在の最小値 minNum 以上の要素は、スコアを下げることなく範囲に取り込めます。そこで、minNum 未満の要素に行き当たるまでポインタを進めます。
  • 各段階で「現在の幅 × 現在の最小値」を計算し、答えを更新します。
  • その後、両端のうち大きい方の値を新しい最小値の候補として採用し、再度拡張を行います。これにより、あり得るすべての「最小値の水準」を漏れなく評価できます。

アルゴリズムの手順

  1. ans と minNum を nums[k] で初期化する。
  2. ポインタ i と j をどちらも k に設定する。
  3. i > -1 または j < 配列のサイズ である間、次を繰り返す。
    • nums[i] ≥ minNum である限り、i を左へ移動する。
    • nums[j] ≥ minNum である限り、j を右へ移動する。
    • ans を max(ans, (j − i − 1) × minNum) で更新する。
    • minNum を、範囲外の場合は -1 として、nums[i] と nums[j] の大きい方で更新する。
  4. ans を返す。

Pythonでの実装例

理解を深めるために、以下の実装を見てみましょう。

def solve(nums, k):
    ans = nums[k]
    minNum = nums[k]
    i = k
    j = k
    while i > -1 or j < len(nums):
        while i > -1 and nums[i] >= minNum:
            i -= 1
        while j < len(nums) and nums[j] >= minNum:
            j += 1
        ans = max(ans, (j - i - 1) * minNum)
        minNum = max(nums[i] if i > -1 else -1,
                     nums[j] if j < len(nums) else -1)
    return ans

nums = [2,5,4,8,5,6]
k = 3
print(solve(nums, k))

実行結果

入力:

[2,5,4,8,5,6], 3

出力:

20

計算量

  • 時間計算量: O(n)。左右のポインタはそれぞれ配列を一度だけ走査するため、全体で線形時間で処理できます。
  • 空間計算量: O(1)。追加のデータ構造は一切不要です。

まとめ

「最小値 × 長さ」で定義されるスコアの最大化問題は、単調な拡張戦略(貪欲法)によって効率的に解けます。k を必ず含むという制約があるため、k を起点に左右へ領域を広げるこの手法が自然な選択になります。同様のパターンは LeetCode の「Maximum Score of a Good Subarray」などの問題にも応用できるので、ぜひマスターしておきましょう。

  1. Pythonで部分配列の合計をmで割った余りの最大値を求めるプログラム

    問題の概要 n個の要素からなる配列 nums と整数 m が与えられたとき、任意の部分配列(連続する要素の集合)の合計を m で割った余りの最大値を求めることを考えます。 たとえば、nums = [1,5,7,3]、m = 5 が入力として与えられた場合、出力は 3 になります。すべての部分配列について余りを計算すると、次のようになります。 [1] mod 5 = 1 [5] mod 5 = 0 [7] mod 5 = 2 [3] mod 5 = 3 [1,5] mod 5 = 1 [5,7] mod 5 = 2 [7,3] mod 5 = 0 [1,5,7] mod 5 = 3 [5,7,

  2. Pythonで制約付きの建物の最大高さを求めるプログラム

    問題の概要整数 n と制約リスト restrictions が与えられたとします。私たちは都市に n 棟の新しい建物を一列に建てようとしていますが、高さに関するいくつかの制限があります。建物には左から順に 1 から n までの番号が付けられており、各制約は restrictions[i] = (id_i, max_height_i) の形式で表され、「id_i 番の建物の高さは max_height_i 以下でなければならない」ことを意味します。建物の高さに関する都市の規則は以下のとおりです。各建物の高さは 0 以上でなければなりません。1 番の建物(最初の建物)の高さは必ず 0 です。隣接す