1. 標準例 [10,9,2,5,3,7,101,18] -> 4
  2. [0,1,0,3,2,3] -> 4
  3. 全て同値 [7,7,7,7] -> 1
  4. 単調増加 [1,2,3,4,5] -> 5
  5. 単調減少 [5,4,3,2,1] -> 1
  6. 1 要素 -> 1
コース一覧
コンピューターサイエンス上級:アルゴリズムとデータ構造
最長増加部分列(LIS)の解法 ── AOJ 2430 対応の動的計画法

コンピューターサイエンス上級:アルゴリズムとデータ構造

探索、ソート、木、グラフ、動的計画法、貪欲法など、競技プログラミングや技術面接で問われる高度なアルゴリズムとデータ構造を学べるコースです。基本的な CS の知識を持ち、アルゴリズム力を伸ばしたい学習者や、外資・大手の技術面接対策をしたい方を対象としています。約 13 時間 (1 日 30 分 × 26 日) で 50 レッスンを修了でき、修了後はコーディング面接の実装問題を構造的に解けるようになります。

1
連結リスト
01. リンクリスト構築と長さの計算5分
02. リンクリストの反転5分
03. リンクリストのサイクル検出5分
04. ソート済みリンクリストの merge5分
05. リンクリストの中央ノード取得5分
06. ソート済みリストの重複削除5分
07. 第1章まとめクイズ5分
2
二分木
01. 二分木の in-order 走査5分
02. 二分木の pre-order 走査5分
03. 二分木の post-order 走査5分
04. 二分木の幅優先走査 (BFS)5分
05. 二分木の高さ5分
06. 二分木の平衡判定5分
07. 第2章まとめクイズ — 二分木5分
3
探索木 (BST)
01. BST に値を挿入する5分
02. BST から値を検索する5分
03. BST の最小値と最大値5分
04. BST 妥当性チェック5分
05. BST で k 番目に小さい値5分
06. BST から値を削除する5分
07. 第 3 章 まとめクイズ5分
4
ハッシュとセット
01. hashmap で頻度集計5分
02. キーでグループ化5分
03. two sum (hash で O(n))5分
04. 部分配列の和 = k の個数5分
05. 最長連続部分列5分
06. 集合の積 (intersection)5分
07. 第4章まとめクイズ5分
5
グラフ
01. グラフ BFS で連結成分サイズを求める5分
02. グラフ DFS で連結成分の数を数える5分
03. グラフのパス存在判定5分
04. BFS で最短経路の長さを求める5分
05. トポロジカルソート5分
06. 2 部グラフ判定5分
07. ダイクストラ法 — 重み付きグラフの最短経路15分
08. 第5章まとめクイズ — グラフ5分
6
動的計画法 (上級)
01. 編集距離 (レーベンシュタイン距離)5分
02. 最長増加部分列(LIS)の解法 ── AOJ 2430 対応の動的計画法5分
03. 最大部分配列和 (Kadane)5分
04. 隣り合わない最大値 (House Robber)5分
05. グリッド経路数 (Unique Paths)5分
06. 単語分割可能か (Word Break)5分
07. 第6章まとめクイズ — 動的計画法 (上級)5分
7
総合データ構造と関数型
01. map と filter を組み合わせる5分
02. reduce で積を計算5分
03. 関数合成5分
04. カリー化5分
05. trie の単純検索 (prefix マッチ)5分
06. Union-Find (連結成分数)5分
07. 最終総まとめクイズ5分

最長増加部分列(LIS)の解法 ── AOJ 2430 対応の動的計画法

飛ばしてよいぶん、組み合わせが多すぎる

[10, 9, 2, 5, 3, 7, 101, 18] から、順番はそのままに要素を間引いて、増え続ける並びを作ります。一番長いもので何個取れるかが答えです。この場合は 2, 3, 7, 18 の 4 個です。

間引いてよい、というのが厄介です。連続した区間を切り出すだけなら候補は数千通りで済みますが、飛ばしてよいなら「取る・取らない」の組み合わせで 2 の n 乗になります。8 個で 256 通り、40 個で 1 兆通りです。

なお、間引ける並びを部分列、連続した区間を部分配列と呼びます。名前が似ているので、どちらの話をしているのかは常に確かめてください。

「ここで終わる」と決めてしまう

組み合わせが爆発するのは、どこから始まってどこで終わるかを両方とも自由にしているからです。片方を固定します。

「i 番目の要素で 終わる 増加列のうち、一番長いものの長さ」を考えます。終わりを固定すると、i の答えは自分より前だけを見れば決まります。自分より前で、自分より小さい値の要素を探し、その要素で終わる答えのうち一番大きいものに 1 を足すだけです。誰の後ろにも付けないなら、自分 1 個で長さ 1 です。

前の答えしか使わないので、左から順に決めていけます。

Python

nums = [10, 9, 2, 5, 3, 7] i = 5 # nums[i] は 7 for j in range(i): if nums[j] < nums[i]: print(j, nums[j], "の後ろに付けられる")

7 の後ろに付けられるのは 2、5、3 の 3 つです。このうち一番長い答えを持っているのは 5 と 3 (どちらも長さ 2) なので、7 で終わる答えは 3 になります。

左から順に見ていくと、10 は 1、9 も 1、2 も 1、5 は 2、3 も 2、7 は 3。最後に全部の中の最大を取れば、それが答えです。要素ごとに前を全部見返すので手数は要素数の 2 乗で、40 個なら 1,600 回ほど。先ほどの 1 兆通りとは比べものになりません。最後の要素の答えが最大とは限らない ので、この一手を忘れないでください。[1, 2, 3, 0] なら、末尾の 0 の答えは 1 ですが、全体の答えは 3 です。

増加は「厳密」

同じ値は増加とみなしません。[7, 7, 7, 7] の答えは 1 です。比較を <= で書くと 4 になってしまうので、< を使います。ここはテストで必ず落ちる場所です。

もう 1 つ、空の配列を渡されたときに、全体の最大を取る処理が空を見て落ちることがあります。長さを先に確かめておくと安全です。

やってみよう

  • [0, 1, 0, 3, 2, 3] の答えは 4。どの 4 つか、指で追ってみる
  • [5, 4, 3, 2, 1] は 1。減り続けているので、どれも誰かの後ろに付けられない
  • [1, 3, 6, 7, 9, 4, 10, 5, 6] を左から手で追う。答えは 6

要件

  1. 戻り値は厳密増加部分列の最大長 (整数)
  2. DP 配列 dp[i] = i 番目で終わる LIS の長さ、として O(n^2) で求めること
  3. 等しい値は増加とみなさない (nums[j] < nums[i] を満たす場合のみ伸ばす)

入出力例

lengthOfLIS([10,9,2,5,3,7,101,18]) → 4 lengthOfLIS([0,1,0,3,2,3]) → 4 lengthOfLIS([7,7,7,7]) → 1 lengthOfLIS([1,2,3,4,5]) → 5 lengthOfLIS([5,4,3,2,1]) → 1 lengthOfLIS([42]) → 1

ヒント

dp[i] を全て 1 で初期化する (要素 1 つでも長さ 1 の LIS なので)

二重ループで j < i かつ nums[j] < nums[i] のとき dp[i] = max(dp[i], dp[j] + 1)

最終的な答えは dp 配列全体の max

生田 陸人
監修生田 陸人
ゆめさくエンジニア / 現役ソフトウェアエンジニア監修者プロフィールを見る →
編集 ゆめさく編集部·公開 2026/05/19·更新 2026/08/26

関連レッスン

  • 最大部分配列和 (Kadane)

    連続する部分配列の最大和を、Kadane のアルゴリズムで線形時間 O(n) で求めます。

  • 隣り合わない最大値 (House Robber)

    隣り合う要素を同時に選べないという制約の下、配列から取り出せる最大合計を DP で求めます。

  • グリッド経路数 (Unique Paths)

    右と下にのみ進めるグリッドの上で、左上から右下に至る経路の総数を二次元 DP で数えます。

  • 単語分割可能か (Word Break)

    辞書にある単語の連結で対象文字列を構成できるかを判定する Word Break 問題を、DP で線形時間に近い計算量で解きます。

このレッスンに出てくる用語

意味があいまいなまま進んだ語は、ここから読み直せます。

  • 配列サイズ固定の同型データの集まり
  • 素数1 と自分自身でしか割り切れない自然数
  • テストバグを見つける工程
  • 処理計算や代入を表す長方形
  • 戻り値呼び出し元への返答を表す点線矢印
main.py
学習モード
エディタを読み込んでいます

メモ

最長増加部分列(LIS)の解法 ── AOJ 2430 対応の動的計画法

⌘S で保存