https://leetcode.com/problems/minimum-operations-to-make-the-array-k-increasing/
LIS的O(nlogn)解法
个人解答
class Solution:
def kIncreasing(self, arr: List[int], k: int) -> int:
res = 0
for d in range(0, k):
min_tail = [] # min_tail[i]: min tail num with i-length LIS
for i in range(d, len(arr), k):
if i == d:
min_tail.append(arr[i])
continue
idx = bisect.bisect_right(min_tail, arr[i])
if idx == len(min_tail):
min_tail.append(arr[i])
else:
min_tail[idx] = arr[i]
res += len(arr) // k + int(d < len(arr) % k) - len(min_tail)
return res
题目分析
题目需要变通一下,其实就是求k个最长非降的子序列,不过题目给的数据范围大,常规的O(n^2)的解法超时,需要考虑O(nlogn)的解法
这里借鉴LIS题目中的解法:https://leetcode.com/problems/longest-increasing-subsequence/discuss/74824/JavaPython-Binary-search-O(nlogn)-time-with-explanation-time-with-explanation)
创建了一个tail数组,存储不同长度子序列的最小结尾数字,这样可以直接二分找到以当前数字结尾的最长子序列,妙哇!
以[4,5,6,3]
为例:
len = 1 : [4], [5], [6], [3] => tails[0] = 3
len = 2 : [4, 5], [5, 6] => tails[1] = 5
len = 3 : [4, 5, 6] => tails[2] = 6
那么,遇到一个新数字,只要二分这个[3,5,6]
数组,找到对应位置:
- 如果比所有的都大,那么添到最后边
- 如果定位到某个位置i,那么更新i所在的值即可
最终,LIS就是这个数组的长度!