Pythonで「厳密に増加してから減少する」最長部分リスト(山型)の長さを求める方法
問題概要
数値のリスト nums が与えられます。この中から、値が厳密に増加した後に厳密に減少する(山型の形状、最小長3)最長の部分リストの長さを求めます。
例えば、入力が nums = [8, 2, 4, 6, 3, 1] の場合、部分リスト [2, 4, 6, 3, 1] が厳密に増加してから減少しているため、出力は 5 となります。
アルゴリズムの考え方
この問題は、リストを先頭から走査しながら、増加区間と減少区間の長さをそれぞれ記録することで解決できます。手順は以下の通りです。
i := 0、n := リストaのサイズ、res := 負の無限大で初期化します。i < n - 2の間、次の処理を繰り返します。- 区間の開始位置を
st := iとして記録します。 - 増加カウンタ
linc := 0、減少カウンタldec := 0を初期化します。 i < n - 1かつa[i] < a[i + 1]の間、lincを1ずつ増やしながらiを進めます(厳密な増加区間を検出)。i < n - 1かつa[i] > a[i + 1]の間、ldecを1ずつ増やしながらiを進めます(厳密な減少区間を検出)。linc > 0かつldec > 0の場合(増加と減少の両方が存在する場合)、res := max(res, i - st + 1)として答えを更新します。i < n - 1かつa[i] == a[i + 1]の間、iを進めて等しい要素をスキップします。
- 区間の開始位置を
- 最後に、
res >= 0であればresを返し、条件を満たす部分リストが存在しなければ0を返します。
実装例
それでは、実際のPythonコードを見て理解を深めましょう。
class Solution:
def solve(self, a):
i, n, res = 0, len(a), float("-inf")
while i < n - 2:
st = i
linc, ldec = 0, 0
while i < n - 1 and a[i] < a[i + 1]:
linc += 1
i += 1
while i < n - 1 and a[i] > a[i + 1]:
ldec += 1
i += 1
if linc > 0 and ldec > 0:
res = max(res, i - st + 1)
while i < n - 1 and a[i] == a[i + 1]:
i += 1
return res if res >= 0 else 0
ob = Solution()
nums = [8, 2, 4, 6, 3, 1]
print(ob.solve(nums))
入力
[8, 2, 4, 6, 3, 1]
出力
5
計算量について
このアルゴリズムはリストを先頭から末尾まで一度だけ走査するため、時間計算量は O(n) となります。また、使用する変数はごくわずかなので、空間計算量も O(1) で済みます。隣接要素同士の比較だけで山型のパターンを検出できる、シンプルかつ効率的な手法です。
-
Pythonで「最小値×2>最大値」を満たす最長の部分リストの長さを求めるプログラム
数値のリスト nums が与えられたとき、「部分リスト内の最小値 × 2 > 部分リスト内の最大値」という条件を満たす、最長の連続した部分リスト(サブリスト)の長さを求める問題を考えてみましょう。たとえば、nums = [10, 2, 6, 6, 4, 4] という入力の場合、出力は 4 になります。これは、部分リスト [6, 6, 4, 4] が「2 × 4 > 6」という条件を満たす最長の部分リストだからです。解法のアプローチ:スライディングウィンドウと単調両端キューこの問題は、スライディングウィンドウ(尺取り法)と単調な両端キュー(deque)を組み合わせることで効率的に解けます。各時点
-
Pythonで1つの要素を削除して作れる最長の連続増加サブリストの長さを求める方法
問題の概要 数値のリスト nums が与えられます。ここで、リストから0個または1個の要素を削除できるものとし、その結果として得られる「連続した厳密に増加する部分リスト(サブリスト)」の最大の長さを求めます。 たとえば、入力が nums = [30, 11, 12, 13, 14, 15, 18, 17, 32] の場合、答えは 7 になります。18 を削除すれば [11, 12, 13, 14, 15, 17, 32] という最も長い連続した厳密増加部分リストが得られ、その長さがちょうど 7 になるためです。 解法の考え方 この問題は、次の2つの配列を用意すると効率よく解けます。 pre