動的計画法 (DP) と二分探索で最長の増える部分列を求める

整数の​並びから、​順を​保ったまま​飛び​飛びに​選んで、​どんどん​大きくなる​数の​列を​作る​とき、​その​最も​長い​長さを​求める。​O(n²) の​ DP から​入り、​長さごとの​「最も​小さい​末尾」を​二分探索で​直す O(n log n) に​縮めます。

海外で定番の問題リスト 4 本のうち 3 本に入っています (Blind 75、NeetCode 150、LeetCode Top Interview 150)

問題

整数の並び nums を受け取ります。nums からいくつかの数を、順を変えずに選び出して (間を飛ばしてかまいません) 列を作ります。選んだ列が、前より後ろが必ず大きい (等しいのは不可) とき、その列の長さとして最も長いものを返す関数を書いてください。

  • 長さを返せばよく、列そのものは返しません
  • nums の長さは 1 以上 2500 以下、値はどれも絶対値が 10000 以下です
  • 書き終えたら、O(n²) より速い書き方も示してください

入力と出力の例

例 1

1、2、5、9 を選ぶと長さ 4 です。5 つ以上の増える列は作れません。

入力

nums = [3, 1, 8, 2, 5, 9, 4]

出力

4

例 2

等しい数は増えたことにならないので、1 つしか選べません。

入力

nums = [5, 5, 5]

出力

1

例 3

2、3、4、5 を選んで長さ 4 です。最後の 1 は、どの列も伸ばせません。

入力

nums = [2, 6, 8, 3, 4, 5, 1]

出力

4

面接での聞かれ方

面接では、まず O(n²) を書かせ、そこから縮められるかを聞く流れが多いです。

  • 「配列の最長増加部分列の長さを返してください」
  • O(n²) の DP を書き終えると、「n が 10 万なら?」「O(n log n) にできますか」と聞かれます。シニアの面接では、ここからが本番です
  • 「速い書き方の途中の配列は、答えの列そのものですか」と、仕組みを分かっているかを確かめられます
  • 「長さではなく列そのものを返すには?」と続くこともあります

「部分列」は飛び飛びでよく、「部分配列」は続いた区間です。どちらかを最初に確かめ、等しい数を増えたことにするか (狭義か広義か) も合わせて確かめます。

アルゴリズムのチートシートの「動的計画法 (DP)」に、​この​型の​要点を​表と​コードで​まとめています。

main.py
def lis_length(nums):
    # ここに書きます
    pass


# 「実行」を押すと、この行の結果が出ます
print(lis_length([3, 1, 8, 2, 5, 9, 4]))

できているか「例で確かめる」を押すと、問題の例を全部この関数に渡して、通ったかをここに出します