变长滑动窗口法
求解思路:
1.使用双指针的技巧,初始化左右指针left,right,规定一个窗口;
2.第一个循环:首先滑动右指针,并更新状态(窗口内元素状态,及对题目要求是否满足等),直到窗口内的元素满足题目要求时进入第二个循环;
3.第二个循环:滑动左指针,并更新状态,知道窗口内的元素不满足题目要求时退出第二个循环;
4.重复2、3步,直到窗口不能再扩展。
tips:两个循环的状态是对称的,一个往窗口中添加元素,另一个从窗口中删除元素。
例题
```java
class Solution {
public static String minWindow(String s, String t){
int minLeftIndex = -1;
int minRightIndex = -1;
int sLen = s.length();
int tLen = t.length();
// 如果slen<tlen,则不包含这样的子串
if(sLen < tLen){
return "";
}
// 变长滑动窗口
int left = 0;
int right = 0;//滑动窗口初始位置
Map<Character,Integer> tMap = new HashMap<>();
Map<Character,Integer> sMap = new HashMap<>();
for (char c : t.toCharArray()) {
int count = tMap.getOrDefault(c, 0)+1;
tMap.put(c, count);
}
// valid记录还有多少个字符待匹配
int valid = tMap.size();
while(right < sLen){
// 更新sMap中的内容
char rightChar = s.charAt(right);
right++;
if(tMap.containsKey(rightChar)){
sMap.put(rightChar,sMap.getOrDefault(rightChar, 0)+1);
// 若需要的字符数量与已经包含的字符数量相等,
if(sMap.get(rightChar).equals(tMap.get(rightChar))){
valid--;
}
}
// 当当前窗口已经包含了t所有的字符,收缩窗口
while(valid==0 && (right-left)>=tLen){
if(minRightIndex == -1 || minRightIndex-minLeftIndex > right-left){
minLeftIndex = left;
minRightIndex = right;
}
char leftChar = s.charAt(left);
if(tMap.containsKey(leftChar)){
if(sMap.get(leftChar).equals(tMap.get(leftChar))){
valid++;
}
sMap.put(leftChar,sMap.get(leftChar)-1);
}
left++;
}
}
return minLeftIndex==-1 ? "" : s.substring(minLeftIndex,minRightIndex);
}
}
class Solution {
public boolean checkInclusion(String s1, String s2) {
int left = 0;
int right = 0;
Map<Character,Integer> map1 = new HashMap<>();
Map<Character,Integer> map2 = new HashMap<>();
for (char c : s1.toCharArray()) {
map1.put(c, map1.getOrDefault(c, 0)+1);
}
// int valid = map1.size();
int valid =0;
while(right<s2.length()){
char rightChar = s2.charAt(right);
right++;
if(map1.containsKey(rightChar)){
map2.put(rightChar, map2.getOrDefault(rightChar, 0)+1);
if(map1.get(rightChar).equals(map2.get(rightChar))){
valid++;
}
}
while(right-left>=s1.length()){
if(valid==map1.size()){
return true;
}
char leftChar = s2.charAt(left);
if(map1.containsKey(leftChar)){
if(map1.get(leftChar).equals(map2.get(leftChar))){
valid--;
}
map2.put(leftChar, map2.get(leftChar)-1);
}
left++;
}
}
return false;
}
}
class Solution {
public List<Integer> findAnagrams(String s, String p) {
List<Integer> res = new ArrayList<>();
if(s.length()<p.length()){
return res;
}
Map<Character,Integer> mapS = new HashMap<>();
Map<Character,Integer> mapP = new HashMap<>();
for(char c:p.toCharArray()){
mapP.put(c, mapP.getOrDefault(c, 0)+1);
}
int left = 0;
int right = 0;
while(right<s.length()){
char rightChar = s.charAt(right);
right++;
if(mapP.containsKey(rightChar)){
mapS.put(rightChar, mapS.getOrDefault(rightChar, 0)+1);
}
while(right-left>=p.length()){
if(mapP.equals(mapS)){
res.add(left);
}
char leftChar = s.charAt(left);
if(mapP.containsKey(leftChar)){
mapS.put(leftChar, mapS.get(leftChar)-1);
}
left++;
}
}
return res;
}
}
class Solution {
public int lengthOfLongestSubstring(String s) {
Map<Character,Integer> map = new HashMap<>();
int res = 0;
int left = 0;
int right = 0;
// int index = 0;//上一次出现重复字符的位置
while(right<s.length()){
char rightChar = s.charAt(right);
right++;
if(!map.containsKey(rightChar)){
map.put(rightChar, 1);
res = res > (right - left) ? res : (right-left);
}else{
while(map.containsKey(rightChar)){
char leftChar = s.charAt(left);
left++;
// map.put(leftChar, map.get(leftChar)-1);
map.remove(leftChar, 1);
}
map.put(rightChar, 1);
}
}
return res;
}
}