Cho danh sách số nums, tìm độ dài dãy con tăng dần dài nhất (LIS), giả định dãy con có thể "quay vòng" về đầu danh sách (circular).
Ví dụ: nums=[6,5,8,2,3,4] → output 5 (LIS vòng tròn: [2,3,4,6,8]).
Ý tưởng: Nối mảng với chính nó a = nums + nums. Với mỗi vị trí bắt đầu i trong n phần tử đầu, tìm LIS trên đoạn a[i : i+n] (độ dài n). Max của tất cả là đáp án.
Thuật toán LIS O(n log n) với binary search:
- Duy trì mảng
dplưu giá trị cuối nhỏ nhất của dãy con tăng độ dàilen - Với mỗi
n: tìm vị trík = bisect_left(dp, n) - Nếu
k == len(dp):dp.append(n) - Ngược lại:
dp[k] = n len(dp)= độ dài LIS hiện tại
Triển khai
import bisect
class Solution:
def solve(self, nums):
a = nums + nums
ans = 0
for i in range(len(nums)):
dp = []
for j in range(i, len(nums) + i):
n = a[j]
k = bisect.bisect_left(dp, n)
if k == len(dp):
dp.append(n)
else:
dp[k] = n
ans = max(ans, len(dp))
return ans
ob = Solution()
nums = [4, 5, 8, 2, 3, 4]
print(ob.solve(nums))
Đầu vào
[4, 5, 8, 2, 3, 4]
Đầu ra
5
Phân tích: Phức tạp O(n² log n). Có thể tối ưu bằng Segment Tree / Fenwick Tree trên giá trị nén (coordinate compression) về O(n log n) tổng, nhưng phức tạp hơn. Với n ≤ 1000, O(n² log n) chấp nhận được.