描述
给两个数组arr1
和arr2
, 里面的数字都是正整数. 可以用arr2
中的数字替换掉arr1
中的任意数字(不限次数), 问最少多少次替换能够将arr1
改变成严格递增数组.
思路
最长递增子串, bottom-up
找LIS的长度很简单… 如果问最少修改多少个数组元素, 使得原数组严格递增呢?
思路是开一个数组LIS
, LIS[i]
表示以下标i
结尾的有效的最长递增子序列长度. 有效指的是我们可以修改i
之前的一些值, 来让数组递增到arr[i]
.
但是有一个限制条件, j
和i
不但要满足arr[j] < arr[i]
, 这两个值的gap
还要允许放入足够的整数在中间, 也就是arr[i] - arr[j] >= i - j
,
否则就算arr[i] > arr[j]
, 例如[1,2,2,2,2,3]
中3 > 1
但是无法通过修改中间的值(2
)来让整个数组严格递增.
(最少的修改次数来让数组严格递增)代码
1 | def minChange(arr, n): |
利用上面的思路, 对于本题, 我们可以判断arr1
中每两个点, 是否可以利用arr2
中的点(把arr2
排序二分搜索, 两个坐标一左一右比一下)来补充他们之间的gap
. 如果可以, 那么这两个点是可以保留(不修改)的.
还需要注意的是, 本题还要判断是否可以修改, 因为存在一些无论如何也不能变为严格递增的情况. 这样我们需要用一个valid
数组, valid[i]
表示是否可以处理到i
这里.
对于例如arr1 = [0,0,0,0,1], arr2 = [1,2,3,4,5]
, 显然全换掉就行. 但是如果单纯跑一次LIS的思路, valid[-1]
会是False
, 因为在原始数组缺少一个足够大的数(和足够小的数), 来作为搜索arr2
数组的依据. 所以需要在arr1
前后各加一个padding.
dfs+memo, top-down
比赛的时候本来要写出来了, 然后头脑不清醒中途放弃, 想了半天别的更难想的思路…真実的菜鸡:(
对于每个点, 记录之前的数字pre
:
- 如果当前数字小于等于
pre
, 那么必然需要改变当前数字, 那么二分搜索arr2
, 找比pre
大的最小的数(Python的bisect_right
).
如果存在, 那么这是最优解. 否则直接返回无穷大. - 如果当前数字大于
pre
, 那么可以修改, 也可以不改(两个分支). 修改的话和上面逻辑一样. 不修改的话, 本轮的数字作为新的pre
继续搜. 然后返回最优的分支.
contest的时候默认为当前大于pre
就一定不用改了, 但是那样处理不了arr1=[5,6,7,1], arr2=[2,3,4,5]
的情况.
代码
最长递增子串
1 | class Solution(object): |
dfs+memo
1 | class Solution(object): |