1. 滑动窗口
题目中要求的是“子数组”,但实际上是要求连续的“子串”。
因此可以使用滑动窗口,固定左侧,右侧在满足“湍流”条件的情况下向右移动,直到不满足条件,记录此时子数组的长度。
取长度的最大值,然后重新开始窗口的滑动。
由于当前窗口内的子数组满足“湍流”条件,因此该子数组的任意子数组也满足“湍流”条件,故调整窗口左侧到右侧位置,以窗口最后一个元素作为下一个窗口新开始的元素。
时间O(n)
,空间O(1)
。
1 | class Solution { |
题目中要求的是“子数组”,但实际上是要求连续的“子串”。
因此可以使用滑动窗口,固定左侧,右侧在满足“湍流”条件的情况下向右移动,直到不满足条件,记录此时子数组的长度。
取长度的最大值,然后重新开始窗口的滑动。
由于当前窗口内的子数组满足“湍流”条件,因此该子数组的任意子数组也满足“湍流”条件,故调整窗口左侧到右侧位置,以窗口最后一个元素作为下一个窗口新开始的元素。
时间O(n)
,空间O(1)
。
1 | class Solution { |
遍历直到出现不满足非递减的情况,此时需要进行调整,然后看调整之后是否可以继续满足非递减条件。
调整时使用贪心策略,优先修改能使调整后的当前位置的元素更小。
可能遇到的情况:1 2 3 4 1 6 7 ...
, 1 2 3 8 4 5 6 ...
时间O(n)
,空间O(1)
。
1 | class Solution { |
由于不能贪心抽两侧最大的牌,因此考虑dp,两侧都抽,然后对剩下的部分同样进行一次“可获得的最大点数”的求解。
为了避免重复计算,使用hash表记录求解过的每一段数组的最大点数。
另外,由于hash表的key是pair
类型,因此还需要手动提供pair
类型的hash函数。
时间O(k^2)
,空间O(k^2)
,因为要求解和记录k从0到k每种情况下,对应数组段的最大点数。超时。
1 | struct pairhash { |
并不一定是窗口两侧初始化为数组两侧,窗口向内收缩;也可以是固定窗口大小,移动窗口位置。
因为要从n个数中选k个,因此可以固定窗口大小为n-k个,代表剩余的数据。窗口从左向右滑动,在此过程中统计结果。
时间O(k)
,空间O(1)
。
滑动窗口从左侧开始在满足cost的情况下向右滑动,记录最大窗口大小。
当cost不足以支持窗口继续向右扩展时,左侧窗口向右收缩1次,然后右侧窗口继续向右扩展。
时间O(n)
,空间O(1)
。
1 | class Solution { |
每移动一下窗口,就对新窗口的内容排序,得到中位数。
时间O(nklogk)
,超时。
1 | class Solution { |
由于移动窗口之后,绝大多数的数据是不变的,因此对窗口内的所有数据重新排序是很费时的。
考虑用一个大根堆和一个小根堆储存有序数组左右两侧的部分,大根堆存较小的数据,小根堆存较大的数据,使得大小根堆的堆顶都是有序数据中间的部分。
这样如果窗口是奇数大小,则令大根堆比小根堆多1个数,大根堆的堆顶就是中位数;如果窗口大小是偶数,则取二者的平均值。
当窗口向右移动时,右侧新增的元素如果>=大根堆堆顶元素,则加入大根堆,否则加入小根堆。
窗口向右移动之后,左侧减少了一个元素。假设已经从堆中删除了这个元素,则可能会导致两个堆各自的元素个数不再满足要求,因此需要弹出数据偏多的堆的堆顶元素,加入数据偏少的堆中。
考虑删除左侧元素。因为窗口左侧的元素可能在堆的内部,而堆只能对堆顶进行操作,所以不能直接删除可能在内部的窗口左侧元素。又因为如果不考虑数据个数的话,在堆内部的这个元素对于求中位数是没有影响的,因此可以考虑延迟删除。
使用一个hash表记录每个元素要删除的次数,当要删除的元素在堆的内部时,暂时不删除,因为对于求解问题没有影响。直到在堆将要删除的元素移动到堆顶时,才可能会对中位数的求解产生影响,此时检查hash表,将其删除即可。
时间O(nlogk)
。
首先找到第一个字符最后一次出现的位置,得到一个子串。
然后遍历这个子串,找其中每一个字符的最后一次出现的位置,得到这个子串最终的结尾。
对右侧剩余部分做同样的处理。
时间O(n^2)
,空间O(1)
。
1 | class Solution { |
上述方法的问题是在查找每个元素出现的最后一个位置上花费了太多的时间,导致时间复杂度是O(n^2)
的。
可以使用一个bool
数组记录已经访问过最后一个位置的元素,之后再遇到就不需要再次访问了,不过这样最差是O(26n)
的。
更好的方法是首先遍历一次字符串,使用一个大小为26的数组记录每个字符最后出现的位置,在需要最后一个位置时直接查表。
时间O(n)
,空间O(26) = O(1)
。
两个指针分别指向name
和typed
即可。
时间O(m + n)
,空间O(1)
。
1 | class Solution { |
直接在原有字符串上进行修改,当遇到#
时,把其左侧的第一个非#
修改为#
,表示已删除。
为了更快的找到左侧第一个非#
字符,而不是每次都要–遍历,可以直接逆序修改。
时间O(n)
,空间O(1)
。
1 | class Solution { |
首先找到正数和负数的分界点,然后二者根据各自指向的值的平方大小向左向右移动。
时间O(n)
,空间O(1)
。
1 | class Solution { |