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 = 0for d in range(0, k):min_tail = [] # min_tail[i]: min tail num with i-length LISfor i in range(d, len(arr), k):if i == d:min_tail.append(arr[i])continueidx = 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] = 3len = 2 : [4, 5], [5, 6] => tails[1] = 5len = 3 : [4, 5, 6] => tails[2] = 6
那么,遇到一个新数字,只要二分这个[3,5,6]数组,找到对应位置:
- 如果比所有的都大,那么添到最后边
- 如果定位到某个位置i,那么更新i所在的值即可
最终,LIS就是这个数组的长度!
