算法随手记Day11-滑动窗口
1.定长子串中元音的最大数目
给你字符串 s 和整数 k 。
请返回字符串 s 中长度为 k 的单个子字符串中可能包含的最大元音字母数。
英文中的 元音字母 为(a, e, i, o, u)。
示例
输入:s = "abciiidef", k = 3
输出:3
解释:子字符串 "iii" 包含 3 个元音字母。
思路
1.首先判断前三个数的元音个数vowel,并设定最大值max,后续判断时取vowel和max中的最大值
2.开始滑动这个窗口,最左面的字母移除,先判断是否是元音,如果是则vowel–,不是则不操作
3.最右面的字母进入,也是判断是否元音,是则vowel++
代码实现
public int maxVowels(String s, int k) {
char[] c = s.toCharArray();
int ans = 0;
int vowel = 0;
for (int i = 0; i < c.length; i++) {
// 1. 进入窗口
if (c[i] == 'a' || c[i] == 'e' || c[i] == 'i' || c[i] == 'o' || c[i] == 'u') {
vowel++;
}
if (i < k - 1) { // 窗口大小不足 k
continue;
}
// 2. 更新答案
ans = Math.max(ans, vowel);
// 3. 离开窗口,为下一个循环做准备
char out = c[i - k + 1];
if (out == 'a' || out == 'e' || out == 'i' || out == 'o' || out == 'u') {
vowel--;
}
}
return ans;
}
2.最小覆盖子串
给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 "" 。
示例
输入:s = "ADOBECODEBANC", t = "ABC"
输出:"BANC"
解释:最小覆盖子串 "BANC" 包含来自字符串 t 的 'A'、'B' 和 'C'。
思路
-
初始化 ansLeft=−1, ansRight=m,用来记录最短子串的左右端点,其中 m 是 s 的长度。
-
用一个哈希表(或者数组)cntT 统计 t 中每个字母的出现次数。
-
初始化 left=0,以及一个空哈希表(或者数组)cntS,用来统计 s 子串中每个字母的出现次数。
-
遍历 s,设当前枚举的子串右端点为 right,把 s[right] 的出现次数加一。
-
遍历 cntS 中的每个字母及其出现次数,如果出现次数都大于等于 cntT 中的字母出现次数:
-
如果 right−left<ansRight−ansLeft,说明我们找到了更短的子串,更新 ansLeft=left, ansRight=right。
-
把 s[left] 的出现次数减一。
-
左端点右移,即 left 加一。
-
重复上述三步,直到 cntS 有字母的出现次数小于 cntT 中该字母的出现次数为止。
-
-
最后,如果 ansLeft<0,说明没有找到符合要求的子串,返回空字符串,否则返回下标 ansLeft 到下标 ansRight 之间的子串。
代码实现
public String minWindow(String s, String t) {
int[] cntS = new int[128]; // s 子串字母的出现次数
int[] cntT = new int[128]; // t 中字母的出现次数
for (char c : t.toCharArray()) {
cntT[c]++;
}
public String minWindow(String s, String t) {
int[] cntS = new int[128]; // s 子串字母的出现次数
int[] cntT = new int[128]; // t 中字母的出现次数
for (char c : t.toCharArray()) {
cntT[c]++;
}
char[] c = s.toCharArray();
int m = c.length;
int ansLeft = -1;
int ansRight = m;
int left = 0;
for (int right = 0; right < m; right++) { // 移动子串右端点
cntS[c[right]]++; // 右端点字母移入子串
while (isCovered(cntS, cntT)) { // 涵盖
if (right - left < ansRight - ansLeft) { // 找到更短的子串
ansLeft = left; // 记录此时的左右端点
ansRight = right;
}
cntS[c[left]]--; // 左端点字母移出子串
left++;
}
}
return ansLeft < 0 ? "" : s.substring(ansLeft, ansRight + 1);
}
private boolean isCovered(int[] cntS, int[] cntT) {
for (int i = 'A'; i <= 'Z'; i++) {
if (cntS[i] < cntT[i]) {
return false;
}
}
for (int i = 'a'; i <= 'z'; i++) {
if (cntS[i] < cntT[i]) {
return false;
}
}
return true;
}

755

被折叠的 条评论
为什么被折叠?



