技巧
- 滑动窗口
- 左闭右开区间[left,right)
void slidingWindow(String s){
HashMap<Character,Integer>window=new HashMap<>();
int left=0,right=0;
while (right<s.length()){
char c=s.charAt(right);
right++;
// 扩大窗口
window.put(c,window.getOrDefault(c,0)+1);
// debug
System.out.printf("window: [%d, %d)\n", left, right);
// ... 更新操作
while (left<right)// 达到需要缩小窗口的条件
{
char d=s.charAt(left);
left++;
window.put(d,window.get(d)-1);
// ... 更新操作
}
}
}
思路
- 定义一个哈希表need和window,need用来存储子串t的字符和字符出现的次数,window用来存储串s中子串t的字符和出现次数
- 按照滑动窗口算法框架:
(1)遍历串s, 扩大窗口right++
(2)当窗口window中各个字符出现次数达到need中个字符出现次数的要求时,缩小窗口left++
(3)选择覆盖子串t的最小窗口长度更新len(right-left<len就更新)
public static String minWindow(String s, String t) {
HashMap<Character,Integer>window=new HashMap<>();
HashMap<Character,Integer>need=new HashMap<>();
int left=0,right=0,valid=0,start=0,len=Integer.MAX_VALUE;
for(char c:t.toCharArray()){
need.put(c,need.getOrDefault(c,0)+1);
}
while (right<s.length()){
char c=s.charAt(right);
right++;
if(need.containsKey(c)){
window.put(c,window.getOrDefault(c,0)+1);
if(need.get(c).equals(window.get(c))){
valid++;
}
}
if(valid==need.size())
System.out.printf("window: [%d, %d)\n", left, right);
while (valid==need.size()){
if(right-left<len){
start=left;
len=right-left;
}
char d=s.charAt(left);
left++;
if(need.containsKey(d)){
if(need.get(d).equals(window.get(d)))
valid--;
window.put(d,window.getOrDefault(d,0)-1);
}
}
}
return len==Integer.MAX_VALUE?"":s.substring(start,start+len);
}
思路
- 定义一个哈希表need和window,need用来存储子串s1的字符和字符出现的次数,window用来存储串s2中子串s1的字符和出现次数
- 按照滑动窗口算法框架:
(1)遍历串s2, 扩大窗口right++
(2)当窗口长度达到字符串s1的长度(大于等于),缩小窗口
(3)如果window中出现的字符和次数与need都能对得上,返回true
public boolean checkInclusion(String s1, String s2) {
HashMap<Character,Integer>need=new HashMap<>();
HashMap<Character,Integer>window=new HashMap<>();
int left=0,right=0,valid=0;
for(char c:s1.toCharArray()){
need.put(c,need.getOrDefault(c,0)+1);
}
while (right<s2.length()){
char c=s2.charAt(right);
right++;
if(need.containsKey(c)){
window.put(c,window.getOrDefault(c,0)+1);
if(window.get(c).equals(need.get(c)))
valid++;
}
while (right-left>=s1.length()){
if(valid==need.size())
return true;
char d=s2.charAt(left);
left++;
if(need.containsKey(d)){
if(need.get(d).equals(window.get(d)))
valid--;
window.put(d,window.get(d)-1);
}
}
}
return false;
}
思路
和上一题同样的思路,使用List存储符合条件的子串起始索引
public List<Integer> findAnagrams(String s, String p) {
List<Integer>res=new ArrayList<>();
HashMap<Character,Integer>need=new HashMap<>();
HashMap<Character,Integer>window=new HashMap<>();
int left=0,right=0,valid=0;
for(char c:p.toCharArray()){
need.put(c,need.getOrDefault(c,0)+1);
}
while (right<s.length()){
char c=s.charAt(right);
right++;
if(need.containsKey(c)){
window.put(c,window.getOrDefault(c,0)+1);
if(need.get(c).equals(window.get(c)))
valid++;
}
while (right-left>=p.length()){
if(valid==need.size()){
res.add(left);
}
char d=s.charAt(left);
left++;
if(need.containsKey(d)){
if(need.get(d).equals(window.get(d)))
valid--;
window.put(d,window.get(d)-1);
}
}
}
return res;
}
思路
- 使用哈希表window存储串s中字符和字符出现的次数
- 按照滑动窗口算法框架
(1)遍历串s,扩大窗口,right++
(2)当前字符在哈希表中出现次数大于1时,说明需要缩小窗口,left++
(3)记录最大的right-left差值更新结果
public int lengthOfLongestSubstring(String s) {
HashMap<Character,Integer>window=new HashMap<>();
int left=0,right=0,max_len=0;
while (right<s.length()){
char c=s.charAt(right);
right++;
window.put(c,window.getOrDefault(c,0)+1);
while (window.get(c)>1){
char d=s.charAt(left);
left++;
window.put(d,window.get(d)-1);
}
max_len=Math.max(max_len,right-left);
}
return max_len;
}