最長増加部分列(LIS)

最長増加部分列 (Spaghetti Source)

最長増加部分列

最長増加部分列 (Longest Increasing Subsequence, LIS) は、数列の部分列として作ることができる単調増加部分列のうち、最も長いのものをいう。
例えば数列 \(\{a_i\}\)
\[2, 3, 1, 7, 5, 2, 3, 0, 5\]

に対して、その最長増加部分列は

\[1, 2, 3, 5\]

となる。

実装

動的計画法と二分探索を用いて、最長増加部分列の長さを計算量 \(\mathcal{O}(n\log{n})\) で求めることができる。

\(dp[i]:\) 長さ \(i\) の単調増加部分列の右端の数字のうち、最小の値

[1]:
from bisect import bisect_left

def lis_length(A, INF=10**9):
    dp = [INF for _ in A]
    for a in A:
        idx = bisect_left(dp, a)
        dp[idx] = a
    return bisect_left(dp, INF)
[2]:
A = [2, 3, 1, 7, 5, 2, 3, 0, 5]
print(lis_length(A))
4

最長増加部分列そのものを求めるには、DPの更新時に更新箇所のインデックスも一緒にメモしておけばよい。

[3]:
from bisect import bisect_left

def LIS(A, INF=10**9):
    dp = [INF for _ in A]
    b = [-1 for _ in A]
    for i in range(len(A)):
        idx = bisect_left(dp, A[i])
        dp[idx] = A[i]
        b[i] = idx + 1
    l = bisect_left(dp, INF)
    seq = [0 for i in range(l)]
    for i in range(len(A)-1, -1, -1):
        if b[i] == l:
            l -= 1
            seq[l] = A[i]
    return seq
[4]:
print(LIS(A))
[1, 2, 3, 5]