Pythonで配列の最大幅ランプを見つける方法:アルゴリズムと実装例
ランプ(ramp)とは?
整数の配列 nums が与えられたとき、「ランプ」とは i < j かつ nums[i] <= nums[j] を満たすインデックスのペア (i, j) のことです。ランプの幅は j − i で表されます。この記事では、nums に含まれるランプの中で最大の幅を求める方法を解説します。条件を満たすペアがひとつも存在しない場合は 0 を返します。
例として、nums = [6,0,8,2,1,5] という入力を考えてみましょう。この場合の出力は 4 になります。(i, j) = (1, 5) のとき nums[1] = 0、nums[5] = 5 となり、条件を満たす幅 5 − 1 = 4 のランプが得られるためです。
解法の考え方
この問題は、次の手順で解くことができます。
- 辞書 B を作成し、各値に対してその値が出現するインデックスの一覧を登録します。i 番目の要素を x = nums[i] とするとき、x がすでに B に存在すれば B[x] の末尾に i を追加し、存在しなければ B[x] = [i] とします。
- mini を [inf](正の無限大)、maxi を [-inf](負の無限大)でそれぞれ初期化します。
- B のキーを昇順に処理し、「mini の末尾の値」と「B[x] の最小値」のうち小さい方を mini に追記します。これにより、各値 x について「x 以下の値が現れる最も左のインデックス」が記録されていきます。
- 続いて B のキーを降順に処理し、「maxi の末尾の値」と「B[x] の最大値」のうち大きい方を maxi に追記します。これにより、各値 x について「x 以上の値が現れる最も右のインデックス」が記録されます。
- maxi を逆順に並べ替えて末尾の初期値を取り除き、mini は先頭の初期値を取り除きます。こうすると、昇順の各キー x に対して「x 以下の最小インデックス(mini)」と「x 以上の最大インデックス(maxi)」が同じ位置で対応します。
- p = 0、res = -inf とし、p が mini の長さに達するまで res = max(res, maxi[p] − mini[p]) を繰り返しながら p を進めます。
- 最終的な res を答えとして返します。
Pythonによる実装例
理解を深めるために、実際のコードを見てみましょう。
def solve(nums):
B = {}
for i in range(len(nums)):
x = nums[i]
if x in B:
B[x].append(i)
else:
B[x] = [i]
mini = [float('inf')]
maxi = [float('-inf')]
for x in sorted(B.keys()):
mini.append(min(mini[-1], min(B[x])))
for x in sorted(B.keys(), reverse=True):
maxi.append(max(maxi[-1], max(B[x])))
maxi = maxi[::-1][:-1]
mini = mini[1:]
p = 0
res = float('-inf')
while p < len(mini):
res = max(res, maxi[p] - mini[p])
p += 1
return res
nums = [6, 0, 8, 2, 1, 5]
print(solve(nums))
入力
[6, 0, 8, 2, 1, 5]
出力
4
まとめ
値ごとの出現位置を辞書に記録し、累積最小値と累積最大値を組み合わせることで、すべてのペアを総当たりすることなく最大幅ランプを効率的に求められます。計算量は O(n log n)(ソート部分が支配的)となり、二重ループによる O(n²) の素朴な解法と比べて大幅に高速です。大きな配列を扱う場合にも実用的なアプローチといえるでしょう。
-
Pythonで制約付きの建物の最大高さを求めるプログラム
問題の概要整数 n と制約リスト restrictions が与えられたとします。私たちは都市に n 棟の新しい建物を一列に建てようとしていますが、高さに関するいくつかの制限があります。建物には左から順に 1 から n までの番号が付けられており、各制約は restrictions[i] = (id_i, max_height_i) の形式で表され、「id_i 番の建物の高さは max_height_i 以下でなければならない」ことを意味します。建物の高さに関する都市の規則は以下のとおりです。各建物の高さは 0 以上でなければなりません。1 番の建物(最初の建物)の高さは必ず 0 です。隣接す
-
Pythonで二分木の各レベルの最大幅を求めるプログラム
二分木が与えられたとき、ツリー内の任意のレベルにおける最大幅を求めることを考えます。ここでいう「レベルの幅」とは、そのレベルにおいて最も左端にあるノードと最も右端にあるノードの間に含まれるノード数のことです。例えば、次のような二分木が入力として与えられた場合を考えてみましょう。この場合、出力は 2 となります。解決のための手順この問題を解くために、以下の手順に従います。各深さにおける位置の最小値と最大値を保持するマップ d を作成します。初期値は、最小値を無限大(∞)、最大値を 0 とします。関数 dfs() を定義します。この関数は引数として root、pos := 0、depth := 0