滑动窗口
定义双指针,这个区间为窗口,不断变换窗口左右端的范围,满足题意条件时进行操作
滑动窗口类型题,大致思路:
class Solution {
public int lengthOfLongestSubstring(String s) {
Map<Character, Integer> need = new HashMap<Character, Integer>();
Map<Character, Integer> window = new HashMap<Character, Integer>();
// 窗口左右断点
int left = 0, right = 0;
//flag:所需字符串中某个字符总数与窗口内某个字符总数的数量相等时flag记录一次
int flag = 0;
//int len = Integer.MAX_VALUE, start = 0;
// 窗口右侧判断越界
while (right < s.length()) {
// 构造窗口字符数量计数器,进行更新等操作
char cr = s.charAt(right);
if (need.containsKey(cr)) {
if (!window.containsKey(cr)) {
window.put(cr, 0);
}
window.put(s.charAt(right), window.get(s.charAt(right)) + 1);
}
//向右扩大窗口
right++;
//判断左侧窗口是否要收缩,while条件根据题意定
while (window.get(s.charAt(right)) > 1) {
// 减去窗口内该字符的一个数量
char cl = s.charAt(left);
window.put(cl, window.get(cl) - 1);
left++;
}
....
}
return ;
}
}
⼒扣第 76 题「最⼩覆盖⼦串」
给你一个字符串 s
、一个字符串 t
。返回 s
中涵盖 t
所有字符的最小子串。如果 s
中不存在涵盖 t
所有字符的子串,则返回空字符串 ""
。
class Solution {
public String minWindow(String s, String t) {
Map<Character, Integer> need = new HashMap<Character, Integer>();
Map<Character, Integer> window = new HashMap<Character, Integer>();
// 构造所需字符数量计数器
for (int i = 0; i < t.length(); i++) {
if (!need.containsKey(t.charAt(i))) {
need.put(t.charAt(i), 0);
}
need.put(t.charAt(i), need.get(t.charAt(i)) + 1);
}
// 窗口内判断
int left = 0, right = 0, flag = 0, len = Integer.MAX_VALUE, start = 0;
while (right < s.length()) {
// 构造窗口字符数量计数器,并向右扩大窗口
if (need.containsKey(s.charAt(right))) {
if (!window.containsKey(s.charAt(right))) {
window.put(s.charAt(right), 0);
}
window.put(s.charAt(right), window.get(s.charAt(right)) + 1);
if (window.get(s.charAt(right)).equals(need.get(s.charAt(right)))) {
flag++;
}
}
right++;
//flag中记录了need窗口中,每个字符所需数量已满足的个数,当flag==need.size()时,代表滑动窗口中字符与need中的字符相等
while (flag == need.size()) {
// 最小长度计算更新
if (right - left < len) {
start = left;
len = right - left;
}
//对滑动窗口内的字符更新
if (need.containsKey(s.charAt(left))) {
window.put(s.charAt(left), window.get(s.charAt(left)) - 1);
if (window.get(s.charAt(left)) < need.get(s.charAt(left))) {
flag--;
}
}
left++;
}
}
return len == Integer.MAX_VALUE ? "" : s.substring(start, start + len);
}
}
//runtime:22 ms
//memory:45.5 MB
⼒扣第 567 题「字符串的排列」
//leetcode submit region begin(Prohibit modification and deletion)
class Solution {
public boolean checkInclusion(String s1, String s2) {
Map<Character, Integer> need = new HashMap<Character, Integer>();
Map<Character, Integer> window = new HashMap<Character, Integer>();
//构造所需字符数量计数器
for (int i = 0; i < s1.length(); i++) {
if (!need.containsKey(s1.charAt(i))) {
need.put(s1.charAt(i), 0);
}
need.put(s1.charAt(i), need.get(s1.charAt(i)) + 1);
}
//窗口内判断
int left = 0, right = 0, flag = 0, len = Integer.MAX_VALUE, start = 0;
//窗口右侧判断
while (right < s2.length()) {
char cr = s2.charAt(right);
//构造窗口字符数量计数器,并向右扩大窗口
if (need.containsKey(cr)) {
if (!window.containsKey(cr)) {
window.put(cr, 0);
}
window.put(cr, window.get(cr) + 1);
//窗口内某个字符数是否和所需的数量相等
if (window.get(cr).equals(need.get(cr))) {
flag++;
}
}
right++;
//
while (right - left >= s1.length()) {
//
if (flag == need.size()) {
return true;
}
char cl = s2.charAt(left);
//判断左侧该字符是否是所需的,需要对滑动窗口进行维护
if (need.containsKey(cl)) {
//需要先更新flag,再更新滑动窗口,否则会造成flag判断不准确
//窗口内某个字符数量可能大于所需数量,因此判断二者窗口内小于所需数量时再停止左侧窗口移动
if (window.get(cl).equals(need.get(cl))) {
flag--;
}
//减去窗口内该字符的一个数量
window.put(cl, window.get(cl) - 1);
}
left++;
}
}
return false;
}
}
//leetcode submit region end(Prohibit modification and deletion)
⼒扣第 438 题「找到字符串中所有字⺟异位词」
//leetcode submit region begin(Prohibit modification and deletion)
class Solution {
public List<Integer> findAnagrams(String s, String p) {
Map<Character, Integer> need = new HashMap<Character, Integer>();
Map<Character, Integer> window = new HashMap<Character, Integer>();
//构造所需字符数量计数器
for (int i = 0; i < p.length(); i++) {
if (!need.containsKey(p.charAt(i))) {
need.put(p.charAt(i), 0);
}
need.put(p.charAt(i), need.get(p.charAt(i)) + 1);
}
//窗口内判断
int left = 0, right = 0, flag = 0, len = Integer.MAX_VALUE, start = 0;
List<Integer> list = new ArrayList<Integer>();
//窗口右侧判断
while (right < s.length()) {
char cr = s.charAt(right);
//构造窗口字符数量计数器,并向右扩大窗口
if (need.containsKey(cr)) {
if (!window.containsKey(cr)) {
window.put(cr, 0);
}
window.put(cr, window.get(cr) + 1);
//窗口内某个字符数是否和所需的数量相等
if (window.get(cr).equals(need.get(cr))) {
flag++;
}
}
right++;
//
while (right - left >= p.length()) {
//
if (flag == need.size()) {
list.add(left);
}
char cl = s.charAt(left);
//判断左侧该字符是否是所需的
if (need.containsKey(cl)) {
//窗口内某个字符数量可能大于所需数量,因此判断二者窗口内小于所需数量时再停止左侧窗口移动
if (window.get(cl).equals(need.get(cl))) {
flag--;
}
//减去窗口内该字符的一个数量
window.put(cl, window.get(cl) - 1);
}
left++;
}
}
return list;
}
}
//leetcode submit region end(Prohibit modification and deletion)
⼒扣第 3 题「⽆重复字符的最⻓⼦串」
class Solution {
public int lengthOfLongestSubstring(String s) {
Map<Character, Integer> window = new HashMap<Character, Integer>();
// 窗口内判断
int left = 0, right = 0, flag = 0, len = 0, start = 0;
// 窗口右侧判断
while (right < s.length()) {
char cr = s.charAt(right);
// 构造窗口字符数量计数器,并向右扩大窗口
if (!window.containsKey(cr)) {
window.put(cr, 0);
}
window.put(cr, window.get(cr) + 1);
right++;
//
while (window.get(cr) > 1) {
// 减去窗口内该字符的一个数量
window.put(s.charAt(left), window.get(s.charAt(left)) - 1);
left++;
}
len = len > (right - left) ? len : (right - left);
}
return len;
}
}