子串
子串
子串指的是一个串中一部分连续元素的集合,是原串的一个子集
例题
和为k的子数组
题目
给你一个整数数组 nums
和一个整数 k
,请你统计并返回 该数组中和为 k
的子数组的个数 。
子数组是数组中元素的连续非空序列。
示例 1:
输入:nums = [1,1,1], k = 2 |
示例 2:
输入:nums = [1,2,3], k = 3 |
提示:
1 <= nums.length <= 2 * 104
-1000 <= nums[i] <= 1000
-107 <= k <= 107
分析
子数组可以被看作两个前缀作差,即arr = longPrefix - shortPrefix
。因此目标值k
可以通过前缀和的差求得,即k = sum[long] - sum[short]
,移项有sum[long] = sum[short] + k
. 同时注意到sum[long]
的求得总是在sum[short]
右侧发生,因此我们可以一边求前缀和并把加上k
的结果(short + k
)存入一张表,一边验证当前的前缀和long
是否等于之前的某个存好的结果;这种情况每出现一次,就说明我们找到了至少一个(可能多个比如有两种short,每出现一次long就会产生两个)子数组。
综上,我们维护一张哈希表,以前缀和加kshort + k
为键,以这个结果值出现的次数为值,一边求和一边维护的行为保证了当前计算的前缀和一定是之前就算好并保存的前缀和的后继,即一定是long
。
代码如下
class Solution { |
滑动窗口最大值
题目
给你一个整数数组 nums
,有一个大小为 k
的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k
个数字。滑动窗口每次只向右移动一位。
返回 滑动窗口中的最大值 。
示例 1:
输入:nums = [1,3,-1,-3,5,3,6,7], k = 3 |
示例 2:
输入:nums = [1], k = 1 |
提示:
1 <= nums.length <= 105
-104 <= nums[i] <= 104
1 <= k <= nums.length
分析
只维护一个一般的线性滑动窗口移动并更新其中最大值会超时。
解法一:使用最大堆。每个位置的数据有两个指标,下标和数值。想象这样一个情景,最初始的窗口在移动,我们每次移动窗口就往窗口中加入一个值,每次获取其中的最大值,只要这个最大值的下标仍在实际的滑动窗口范围内,我们仍然可以继续使用它,否则选拔一个新的最大值,以此类推。为此我们想到可以使用最大堆
,堆顶元素即是最大值,**每次只需要确认堆顶元素是否还在滑动窗口内即可,如果不在,就一直出堆直到堆顶元素在滑动窗口内;滑动窗口移动,就是让新元素入堆。**在一开始初始化一个初始堆(初始窗口)并维护它即可。
堆中元素采用一个数对,即{在数组中的下标,数值}
代码如下
class Solution { |
解法二:使用单调队列。 TBD
最小覆盖子串
题目
给你一个字符串 s
、一个字符串 t
。返回 s
中涵盖 t
所有字符的最小子串。如果 s
中不存在涵盖 t
所有字符的子串,则返回空字符串 ""
。
注意:
- 对于
t
中重复字符,我们寻找的子字符串中该字符数量必须不少于t
中该字符数量。 - 如果
s
中存在这样的子串,我们保证它是唯一的答案。
示例 1:
输入:s = "ADOBECODEBANC", t = "ABC" |
示例 2:
输入:s = "a", t = "a" |
示例 3:
输入: s = "a", t = "aa" |
提示:
m == s.length
n == t.length
1 <= m, n <= 105
s
和t
由英文字母组成
分析
本题可以采用滑动窗口算法。维护一个滑动窗口,不断向右扩张,如果左边界的字符超出了t
中的所有字符,就收缩左边界直到窗口清零或左边界字符仍在t
的字符范围内,每次窗口扩张前都要判断一下是否已经完成覆盖,记录覆盖结果中出现的最小子串。
tip:判断字符数量有没有到达要求可以再维护一个count
数组记录t
中字符出现的次数(也代指窗口中还需要出现的次数),0
即为没有(窗口中不需要)出现;之后在维护窗口时,右边界纳入的字符即代表窗口覆盖到了它,因此count
数组对应位置自减,如果一个位置的计数为负数,说明这个字符在子串中是多余的,左边界可以收缩将它踢出窗口(记得更新count
)。如果count
数组没有正数,说明当前窗口覆盖了t
。
代码如下
class Solution { |
(•‿•)