Pythonで最大k個の0を反転して最長の連続する1の長さを求めるプログラム
0と1のみで構成されるバイナリリストと、整数kが与えられたとします。最大k個の0を1に変更できるとき、すべてが1で構成される最も長い連続区間(部分リスト)の長さを求めるのがこの問題です。
例えば、入力が nums = [0, 1, 1, 0, 0, 1, 1]、k = 2 の場合、出力は 6 になります。中央にある2つの0を1に変更すれば、リストは [0, 1, 1, 1, 1, 1, 1] となり、連続する6つの1が得られるためです。
解決アプローチ:スライディングウィンドウ法
この問題は「スライディングウィンドウ(尺取り法)」と呼ばれる手法を使うことで、O(n)の計算量で効率的に解くことができます。考え方はシンプルで、右端を伸ばしながらウィンドウ内の0の数がkを超えないよう左端を調整し、その都度ウィンドウ幅の最大値を記録していきます。
具体的な手順は以下の通りです。
- 変数を初期化します:
zeros = 0(現在のウィンドウ内の0の数)、ans = 0(答え)、j = 0(ウィンドウの左端インデックス) - リストの各要素について、インデックスiと値nを使って以下を繰り返します。
- nが0であれば、zerosを1増やします。
- zerosがkより大きい間、次の処理を行います。
- nums[j]が0であれば、zerosを1減らします。
- jを1増やしてウィンドウの左端を右へ移動させます。
- 現在のウィンドウ幅
i - j + 1がansより大きければ、ansを更新します。
- 最後にansを返します。
それでは、実際のPythonコードを見て理解を深めましょう。
実装例
class Solution:
def solve(self, nums, k):
zeros = 0
ans = 0
j = 0
for i, n in enumerate(nums):
zeros += n == 0
while zeros > k:
zeros -= nums[j] == 0
j += 1
if i - j + 1 > ans:
ans = i - j + 1
return ans
ob = Solution()
nums = [0, 1, 1, 0, 0, 1, 1]
k = 2
print(ob.solve(nums, k))
入力
[0, 1, 1, 0, 0, 1, 1], 2
出力
6
このアルゴリズムでは、各要素が高々2回(右端として1回、左端として1回)走査されるだけなので、全体の計算量はO(n)、追加のメモリ使用量はO(1)で済みます。バイナリ配列に関する「最大k回の操作で〜を最大化する」という種類の問題には、このスライディングウィンドウのパターンが広く応用できるので、ぜひ覚えておきましょう。
-
Pythonで「a」から始まる連続増加部分文字列の最長長さを求めるプログラム
問題の概要小文字の英字と「?」記号を含む文字列 s が与えられます。各「?」については、削除するか、任意の小文字の英字に置き換えることができます。このとき、「a」で始まる連続して増加する部分文字列(例:abcdef のようにアルファベット順に1文字ずつ進む文字列)の最長の長さを求める必要があります。例えば、入力が s = vta???defke の場合、出力は 6 になります。これは、s を vtabcdefke に変換できるためです。変換後の文字列には abcdef という連続増加部分文字列が含まれており、これが「a」で始まる最長のものとなります。解法のアプローチこの問題は、文字列を一度走査
-
Pythonでn分木の最長パスの長さを求めるプログラムの書き方
各要素が (u, v) という形式を持ち、u が v の親であることを表す辺リストが与えられているとします。このとき、木の中で最も長いパスの長さを求める必要があります。ここでいうパスの長さとは、「そのパスに含まれるノードの総数 + 1」のことです。 たとえば、入力が下図のような n 分木だった場合を考えてみましょう。 この場合の出力は 5 になります。なぜなら、パス [1, 4, 5, 7] には合計 4 つのノードが含まれており、パスの長さは 1 + 4 = 5 となるからです。 解き方のアプローチ この問題は、幅優先探索(BFS)を2回実行するという定番テクニックで効率よく解けます。まず