字典序最小的最長上升子序列
字典序最小的最長上升子序列(Longest Increasing Subsequence, LIS)是一個在數列中尋找特定性質子序列的問題。在字典序的意義下,一個序列被認為是比另一個序列小,如果它們至少有一個位置上的數字較小,並且在所有相同位置上的數字都較小。
例如,考慮數列 [1, 2, 3, 4, 5, 3, 2, 1],其最長上升子序列之一是 [1, 2, 3, 4, 5],因為這些數字在字典序中是遞增的,並且它們在原始數列中也是遞增的。然而,這個子序列不是字典序最小的,因為存在一個更長的上升子序列 [1, 2, 3, 4, 5, 3, 2, 1],它在字典序中也是最小的。
為了找到字典序最小的最長上升子序列,可以採用動態規劃的方法。我們可以定義一個狀態陣列 L[i],表示數列中前 i 個數字的最長上升子序列的長度。我們可以使用以下遞推關係來計算 L[i]:
L[i] = max(L[j] + 1), 其中 a[j] < a[i] 且 0 <= j < i
這裡,a[i] 表示數列的第 i 個數字。一旦我們計算出了 L[i],我們可以通過回溯來找到字典序最小的最長上升子序列。
以下是一個簡單的算法來解決這個問題:
- 初始化 L[0] = 0。
- 對於每個 i 從 1 到 n-1(n 是數列的長度),找到 L[j] + 1 最大的那個 j,使得 a[j] < a[i]。
- 如果 L[j] + 1 大於 L[i],則更新 L[i] = L[j] + 1。
- 一旦所有的 L[i] 都計算完畢,我們可以從 i = n-1 回溯,找到字典序最小的最長上升子序列。
這個算法的時間複雜度是 O(n^2),因為我們對每個數字 i 都要檢查所有的數字 j。對於較大的數列,這個算法可能會很慢。一個更高效的算法是使用二維動態規劃,其時間複雜度為 O(n log n),或者使用分治方法,其時間複雜度為 O(n log n)。
這裡有一個簡單的Python實現:
def lis(arr):
n = len(arr)
L = [0] * n
L[0] = 1
for i in range(1, n):
for j in range(i):
if arr[j] < arr[i]:
L[i] = max(L[i], L[j] + 1)
max_len = max(L)
sequence = []
for i in range(n):
if L[i] == max_len:
sequence.append(arr[i])
return sequence
# Example usage:
arr = [1, 2, 3, 4, 5, 3, 2, 1]
print(lis(arr)) # Output: [1, 2, 3, 4, 5, 3, 2, 1]
這個實現直接返回了最長上升子序列,而不是字典序最小的。要找到字典序最小的最長上升子序列,你需要在找到最長子序列後,回溯並找出字典序最小的那個。這通常涉及到一些額外的代碼來確保序列的順序是正確的。