子串
- 子串要求连续,与子序列不同,子序列不要求连续。
和为k的字串
给你一个整数数组 nums 和一个整数 k ,请你统计并返回 该数组中和为 k 的子数组的个数。
子数组是数组中元素的连续非空序列
- 方法
https://www.bilibili.com/video/BV1gN411E7Zx/?spm_id_from=333.337.search-card.all.click&vd_source=c1e82f2c861119afab65688242bdf6f3
1.求出前缀和数组sums[i],sum[i] = sums[i-1]+nums[i]
2.若nums[i]=10,k为6,且前缀和中出现了2次4,则可以确定的是,每一个前缀和为4的元素之后起到i的和即为我们想要的子串
3.借助哈希表,键为各个前缀和,值为当前该前缀和出现的次数
class Solution {
public int subarraySum(int[] nums, int k) {
int n = nums.length;
int[] sums = new int[n];
sums[0] = nums[0];
for (int i = 1; i < n; i++) {
sums[i] = sums[i-1] + nums[i];
}
HashMap<Integer, Integer> map= new HashMap<>();
map.put(0,1);
int count = 0;
for (int i = 0; i < n; i++) {
count+=map.getOrDefault(sums[i]-k,0);//map.getOrDefault若查询失败会返回默认值而不是null
map.put(sums[i],map.getOrDefault(sums[i],0)+1);
}
return count;
}
}
滑动窗口最大值
- 方法
1.维护一个单调递减栈,即栈底为当前栈内最大的元素,且保证栈内的元素一定在当前遍历的窗口内;
2.窗口每向后移动一次,首先要维护栈内的递减性,然后还要将左侧超出窗口外的元素在栈中移除。
将窗口外的元素移除,分为两种情况:
a.要移除的是当前最大值,则我们要pollLast()
,移除栈底
b.要移除的不是当前最大值,说明在之前已经被弹出过。
换句话说,我们可以判断当前栈底元素下标是否在窗口内(deque.peekLast() <= (i-k)
),若在则deque.pollLast()
,否则直接跳过。
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
int n = nums.length;
if (n == 0) {
return new int[0];
}
int[] res = new int[n - k + 1];
Deque<Integer> deque = new LinkedList<>();
for (int i = 0; i < n; i++) {
//保证栈内从栈顶到栈底为递增的(first为栈顶)
//peek == peekFirst
while (!deque.isEmpty() && nums[i] > nums[deque.peek()]) {
deque.pop();//pop == pollFirst
}
deque.push(i); //push == addFirst
if(i>=k){
//移除窗口外的元素
if(deque.peekLast() <= (i-k)){
deque.pollLast();
}
}
if (i >= k - 1) {
//记录结果
res[i - k + 1] = nums[deque.peekLast()];
}
}
return res;
}
}
最小覆盖子串
给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 “” 。
注意:
对于 t 中重复字符,我们寻找的子字符串中该字符数量必须不少于 t 中该字符数量。
如果 s 中存在这样的子串,我们保证它是唯一的答案。
- 分析
前面已经做过几道关于异位词的题目,若只关心字符串的原材料,我们可以利用一个数组来描述一个字符串的信息。
此处若s 中的子串涵盖 t 所有字符,则该子串的每个原材料数量一定不小于t。 - 方法
维护一个窗口,右边界一直向后扩张,扩张到子串的每个原材料数量都不小于t,即包含t,然后再从左侧缩小,每缩小一次就判断子串是否仍然包含t,直至缩小至不包含,为避免后续有更短的包含t的子串,要继续向后扩张至包含t,再从左侧缩小,在这个同时要维护一对最终结果的左右边界,直至左边界或右边界越界
class Solution {
public String minWindow(String s, String t) {
int[] count_t = count(t);
int[] count_s = new int[128];
int l = 0;
int r = -1;
int l_res = 0;
int r_res = Integer.MAX_VALUE;
while(l < s.length()){
if(has(count_s,count_t)){
if(r-l < r_res-l_res){
l_res = l;
r_res = r;
}
count_s[s.charAt(l)]--;
l++;
}else {
if(r==s.length()-1){
break;
}
r++;
count_s[s.charAt(r)]++;
}
}
return r_res==Integer.MAX_VALUE ? "" : s.substring(l_res,r_res+1);
}
public int[] count(String s){
int[] counts = new int[128];
char[] chars = s.toCharArray();
for (char c: chars) {
counts[c]++;
}
return counts;
}
public boolean has(int[] s,int[] t){
for (int i = 0; i < 128; i++) {
if(s[i]<t[i]){
return false;
}
}
return true;
}
}