思路:运用滑动窗口的思想。
虽然说是滑动窗口,但是在思考的问题中有这么几个疑问:
首先,如果是暴力,那么我们需要用到三重循环,第一重循环用来限制窗口长度,第二重循环用来限制左指针的开始坐标,第三重循环用来控制窗口移动。这样肯定会对于数据大的题目超时;
然后,就考虑到可能需要双指针遍历完之后和哈希表结合一起用,这样用一般来说就会让时间复杂度很低,会使其在O(n)这里。这个想法可靠。
但是,对于第二种想法中一些细节在实现过程中想不通:
1.左指针移动时的条件到底满足什么?
2.在哈希表计数的时候,会考虑到再用一重循环遍历字符串的出现个数,怎么让它不用循环遍历就实现这种判断?
第一点,左指针的移动条件就是当s字符中左指针指向的字符的个数大于t字符串中该字符个数,或者,当s当前左指针指向的字符在t中不存在的时候,我们才会移动左指针,而且这里需要用到循环,因为在移动指针后下一个字符也不一定说不满足这个条件,会继续移动,这样我们需要用循环来实现!
第二点,这一点的答案其实很简单,我们不需要全部遍历,只需要在右指针指向一个字符时看一看该字符在t中是不是存在,并且当前s指向之前的连续字符串的该字符个数<=t字符串中该字符的出现个数,这样的话,我们就可以判断它是t所需要的字符。提到这里,我们需要用一个额外的变量cnt来记录t的必须字符。当这个变量cnt==t.length()时,我们就可以说这时候的字符串时满足提议了,同时我们判断当前的长度是不是比上一次满足条件的字符串的长度要小,然后更新;否则忽略。
注意:在循环判断条件当中,l<r,然后后面的那个或关系是需要括号括起来的,小细节需要注意;并且记录长度的变量len最开始的时候一定要很大才行,这样才能顺利更新。
class Solution {
public String minWindow(String s, String t) {
if(s.length()<t.length())
return "";
Map<Character,Integer>map1=new HashMap<Character,Integer>();
Map<Character,Integer>map2=new HashMap<Character,Integer>();
for(int i=0;i<t.length();i++){
map1.put(t.charAt(i),map1.getOrDefault(t.charAt(i),0)+1);
}
int l=0;int r=0;
int cnt=0;
int len=100010;
String res="";
while(r<s.length()){
map2.put(s.charAt(r),map2.getOrDefault(s.charAt(r),0)+1);
if(map1.containsKey(s.charAt(r))&&map2.get(s.charAt(r))<=map1.get(s.charAt(r))){
cnt++;
}
while(l<r&&(!map1.containsKey(s.charAt(l))||map2.get(s.charAt(l))>map1.get(s.charAt(l)))){
map2.put(s.charAt(l),map2.get(s.charAt(l))-1);
l++;
}
if(cnt==t.length()&&len>r-l+1){
len=r-l+1;
res=s.substring(l,r+1);
}
r++;
}
return res;
}
}