滑动窗口
这里的滑动窗口不是TCP的滑动窗口,这个滑动窗口更像是一个kmp的匹配过程,其实kmp也属于一个滑动窗口,窗口就是一段特定的区域,滑动顾名思义就是该窗口不是一成不变的。即通过动态的改变一个特定的区域,达到提高算法效率的目的。
/**
* 最长不重复子串
*/
class Solution_03 {
public int lengthOfLongestSubstring(String s) {
int len = s.length();
if (len == 0) return 0;
int ans = 0;
Map<Character, Integer> m = new HashMap<>();
for (int start = 0, end = 0; end < len; end++) {
char alpha = s.charAt(end);
if (m.containsKey(alpha)) {
start = Math.max(start, m.get(alpha));
}
m.put(alpha, end + 1);
ans = Math.max(end - start + 1, ans);
}
return ans;
}
}
/**
* 最小覆盖子串
*/
class Solution_076 {
public String minWindow(String s, String t) {
int left = 0, right = 0;
int start = 0, minLen = Integer.MAX_VALUE;
//所需要包含得字符串
Map<Character, Integer> tInclude = new HashMap<>();
for (char c : t.toCharArray()) {
if (tInclude.containsKey(c)) {
tInclude.put(c, tInclude.get(c) + 1);
} else {
tInclude.put(c, 1);
}
}
//类似设置一个滑动窗口
Map<Character, Integer> window = new HashMap<>();
//标志位,用于判断所包含得字符串是否符合要求
int flag = 0;
//开始遍历字符串
while (right < s.length()) {
char c = s.charAt(right);
if (tInclude.containsKey(c)) {
window.put(c, window.containsKey(c) ? window.get(c) + 1 : 1);
if (window.get(c) == tInclude.get(c)) {
flag++;
}
}
right++;
while (flag == tInclude.size()) {
if (right - left < minLen) {
start = left;
minLen = right - left;
}
char temp = s.charAt(left);
if (tInclude.containsKey(temp)) {
window.put(temp, window.get(temp) - 1);
if (window.get(temp) < tInclude.get(temp)) {
flag--;
}
}
left++;
}
}
return minLen == Integer.MAX_VALUE ? "" : s.substring(start, start + minLen);
}
}
/**
* 至多包含两个不同字符的最长子串
*/
class Solution_159 {
public int lengthOfLongestSubstringTwoDistinct(String s) {
if (s.length() == 0) return 0;
int maxLen = -1;
int right = 0, left = 0;
int flag = 0;
Map<Character, Integer> m = new HashMap<>();
while (right < s.length()) {
char c = s.charAt(right);
if (m.containsKey(c)) {
m.put(c, m.get(c) + 1);
} else {
flag++;
m.put(c, 1);
}
right++;
while (flag > 2) {
char c1 = s.charAt(left);
m.put(c1, m.get(c1) - 1);
if (m.get(c1) == 0) {
flag--;
}
left++;
}
if (right - left > maxLen) {
maxLen = right - left;
}
}
return maxLen;
}
}
/**
* 至多包含 K 个不同字符的最长子串
*/
class solution_340 {
public int lengthOfLongestSubstringKDistinct(String s, int k) {
if (s.length() == 0) return 0;
int maxLen = -1;
int right = 0, left = 0;
int flag = 0;
Map<Character, Integer> m = new HashMap<>();
while (right < s.length()) {
char c = s.charAt(right);
if (m.containsKey(c)) {
m.put(c, m.get(c) + 1);
} else {
flag++;
m.put(c, 1);
}
right++;
while (flag > k) {
char c1 = s.charAt(left);
m.put(c1, m.get(c1) - 1);
if (m.get(c1) == 0) {
flag--;
}
left++;
}
if (right - left > maxLen) {
maxLen = right - left;
}
}
return maxLen;
}
}
/**
* 长度最小的子数组
* 给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的连续子数组。如果不存在符合条件的连续子数组,返回 0。
*/
class Solution_209 {
public int minSubArrayLen(int s, int[] nums) {
int right = 0, left = 0;
int minLen = Integer.MAX_VALUE;
int sum = 0;
while (right < nums.length) {
sum = nums[right] + sum;
right++;
while (sum > s) {
sum -= nums[left];
left++;
if (right - left < minLen) {
minLen = right - left;
}
}
}
return minLen;
}
}
/**
* 滑动窗口最大值
*/
class Solution_239 {
private int getMaxFormKNums(int[] nums, int left, int right) {
int flag = left;
while (left < right) {
if (nums[left] >= nums[flag]) {
flag = left;
}
left++;
}
return flag;
}
public int[] maxSlidingWindow(int[] nums, int k) {
if (k > nums.length) return null;
int left = 0, right = 0;
int[] result = new int[nums.length - k + 1];
int flag = getMaxFormKNums(nums, 0, k);
//先找到前k个节点中得最大值
right = k;
result[left] = nums[flag];
while (right < nums.length) {
left++;
if (flag >= left) {
if (nums[flag] < nums[right])
flag = right;
} else {
flag = getMaxFormKNums(nums, left, right + 1);
}
result[left] = nums[flag];
right++;
}
return result;
}
}
/**
* 字符串的排列
*/
class Solution_567 {
public boolean checkInclusion(String s1, String s2) {
int right = 0;
int match = 0;//标志位
Map<Character, Integer> m = new HashMap<>();
for (char c : s1.toCharArray()) {
m.put(c, m.getOrDefault(c, 0) + 1);
}
Map<Character, Integer> window = new HashMap<>();
while (right < s2.length()) {
char c1 = s2.charAt(right);
if (m.containsKey(c1)) {
window.put(c1, window.getOrDefault(c1, 0) + 1);
if (window.get(c1) == m.get(c1)) {
match++;
}
if (match == m.size()) return true;
} else {
window.clear();
match = 0;
}
right++;
}
return false;
}
}
总结:
一般的滑动窗口会有两个指针,一个指向窗口的头,另一个指向窗口的尾部,通过操作头和尾的指针来达到对不回溯的匹配结果。掌握套路很重要。