滑动窗口的真实奥秘
一个窗口在数组上缓缓滑动
奥妙的是下一个窗口可以借鉴上一个窗口的值
字符串常用map存字母对应的位置
1. Maximum Sum Subarray of Size K (easy)
解决问题思路:
构建一个滑动窗口,在数组上缓缓滑动,下一个窗口的值是上一个窗口的值减去滑出的值,加上滑入的值。从而从暴力的O(N*K)变成O(N)
代码:
public static int findMaxSumSubArray(int k, int[] arr)
{
int max=-1;
if(arr.length>=k)
{
int i=0;
int j=k-1;//滑动窗口边界值
int p=arr[i];
int sum=0;
for(int t=i;t<=j;t++)
{
sum=sum+arr[t];
}
max=sum; //初窗口处理
i++;
j++; //到下一个窗口
while(j<arr.length)//直接当成一个窗口到下一个窗口更新过程就行
{
int q=arr[j];
int l=arr[i];
sum=sum-p+q;
if(sum>max)
{
max=sum;
}
p=l;
i++;
j++;
}
}
return max;
}
2.Smallest Subarray with a given sum (easy)
思路
当sum(窗口)>=s时,存储此时窗口长度,缩小窗口 i++;
当sun(窗口)<S时,扩大窗口长度,j++;
代码
public static int findMinSubArray(int S, int[] arr) {
int i=0;
int j=0;
int sum=0;
while(sum<S&&j<arr.length)
{
sum=sum+arr[j];
j++;
}
j--;
int k=j-i+1;
if(sum<S)
{
return -1;
}
while(j<arr.length)
{
if(sum>=S)
{
if(j-i+1<k)
{
k=j-i+1;
}
sum=sum-arr[i];
i++;
}
else {
j++;
if(j<arr.length)
sum=sum+arr[j];
}
}
return k;
}
3.Longest Substring with K Distinct Characters (medium)![在这里插入图片描述](https://img-blog.csdnimg.cn/20200328231816844.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2hjaDk3Nw==,size_16,color_FFFFFF,t_70)
思路:
依然是滑动窗口
用map存取窗口内每个字母最后出现的位置
每次窗口变化时:
- i更新到min(每个字母最后出现的位置)+1
并把去掉的字母从map中去除 - j更新到k个字母时的最远位置
变化过程中,存取下滑动窗口最长值
public static int LongestSubstringKDistinct(String s,int k)
{
int len=-1;
int i=0;
int j=0;
Map<Character, Integer> map=new HashMap<>();
int t=0;
while(map.size()<=k&&j<s.length())
{
char a=s.charAt(j);
if(map.containsKey(a))
{
map.put(a,j);
}
else {
if(map.size()==k)
{
break;
}
map.put(a,j);
}
j++;
}
j--;
int maxlength=j-i+1;
System.out.println("i:"+i);
System.out.println("j:"+j);
while(j<s.length())
{
i=minn(map);
map.remove(s.charAt(i));
i=i+1;
System.out.println("i:"+i);
while(map.size()<=k&&j<s.length())
{
j++;
if(j>=s.length())
break;
char a=s.charAt(j);
if(map.containsKey(a))
{
map.put(a,j);
}
else {
if(map.size()==k)
{
j--;
break;
}
map.put(a,j);
}
}
if(j>=s.length())
break;
System.out.println("j:"+j);
if(j-i+1>maxlength)
{
maxlength=j-i+1;
}
}
return maxlength;
}
public static int minn(Map<Character, Integer> map)
{
int minn=Integer.MAX_VALUE;
for(int value:map.values())
{
if(value<minn)
{
minn=value;
}
}
return minn;
}
4.水果成篮
思路:跟上面一题完全一样的思路,只不过k=2
代码:
class Solution {
public static int minaddress(Map<Integer,Integer> map)
{
int min=Integer.MAX_VALUE;
for(int value:map.values())
{
if(value<min)
{
min=value;
}
}
// System.out.println("min:"+min);
return min;
}
public int totalFruit(int[] tree) {
int i=0;
int j=0;
Map<Integer, Integer> map=new HashMap<>();
while(j<tree.length)
{
int n=tree[j];
if(map.containsKey(n))
{
map.put(n, j);
}
else
{
if(map.size()==2)
{
j--;
break;
}
map.put(n, j);
}
j++;
}
if(j==tree.length)
{
j--;
}
// System.out.println("i:"+i);
// System.out.println("j:"+j);
int maxnum=j-i+1;
// System.out.println("maxnum:"+maxnum);
while(j<tree.length&&i<j)
{
i=minaddress(map);
map.remove(tree[i]);
i++;
while(j<tree.length)
{
j++;
if(j==tree.length) {
j--;
break;
}
int n=tree[j];
if(map.containsKey(n))
{
map.put(n, j);
}
else
{
if(map.size()==2)
{
j--;
break;
}
map.put(n, j);
}
}
if(j-i+1>maxnum)
{
maxnum=j-i+1;
}
// System.out.println("i:"+i);
// System.out.println("j:"+j);
// System.out.println("maxnum:"+maxnum);
}
return maxnum;
}
}
无重复字符的最长子串
思路:
滑动窗口,用map保存字符串中每个字母的最后位置。
窗口更新时,j往后移动一位,假如s(j)在前面已经有了,i更新到这个字母在之前的最后的位置+1。
窗口变换过程中,保存窗口最大长度。
代码:
class Solution {
public int lengthOfLongestSubstring(String s) {
int i=0;
int j=0;
Map<Character, Integer> map=new HashMap<>();
while(j<s.length()&&!map.containsKey(s.charAt(j)))
{
map.put(s.charAt(j), j);
j++;
}
j--;
// System.out.println("i:"+i);
// System.out.println("j:"+j);
int maxlength=j-i+1;
while(j<s.length())
{
j++;
if(j==s.length())
break;
char c=s.charAt(j);
// System.out.println("c:"+c);
// System.out.println("map.get(c):"+map.get(c));
if(map.containsKey(c))
{
if(map.get(c)>=i)
{
i=map.get(c)+1;
}
}
map.put(c,j);
// System.out.println("i:"+i);
// System.out.println("j:"+j);
if(j-i+1>maxlength)
{
maxlength=j-i+1;
}
}
return maxlength;
}
}
5.最小覆盖子串
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-I4g2atpX-1585408468048)(A25FBD7F6BAC4733B251F101FE40EE41)]
…
T=“AAABBBCCC”
代表着s中要有A,B,C
(我不管,我就把这题当成这样做了,不想改了。)
…
思路:
滑动窗口
初始窗口:找到包含全部目标字母的第一个字符串,j在移动过程中若有跟边界字母(s(i))相同的,则i更新到下一个字母
窗口更新:
i更新到下一个字母
j找到i移动去掉的原边界字母,当然j在移动过程中若有跟边界字母(s(i))相同的,则i更新到下一个字母。
代码:
class Solution {
public String minWindow(String s, String t) {
int i=0;
int j=0;
Map<Character,Integer> map=new HashMap<>();
Set<Character> diCharacters=new HashSet<>();
for(int l=0;l<t.length();l++)
{
diCharacters.add(t.charAt(l));
}
while(i<s.length()&&!diCharacters.contains(s.charAt(i)))
{
i++;
}
char d=s.charAt(i);
map.put(d,i);
while(map.size()!=diCharacters.size())
{
j++;
if(j==s.length())
return "";
char h=s.charAt(j);
if(h==d)
{
map.put(h,j);
i=minaddress(map);
d=s.charAt(i);
}
else if(diCharacters.contains(h))
{
map.put(h, j);
}
}
// System.out.println("i:"+i);
// System.out.println("j:"+j);
int mini=i;
int minj=j;
while(j<s.length()&&i<j)
{
map.remove(s.charAt(i));
i=minaddress(map);
d=s.charAt(i);
while(map.size()!=diCharacters.size())
{
j++;
if(j==s.length())
break;
char h=s.charAt(j);
if(h==d)
{
map.put(h,j);
i=minaddress(map);
d=s.charAt(i);
}
else if(diCharacters.contains(h))
{
map.put(h, j);
}
}
if(j==s.length())
break;
// System.out.println("i:"+i);
// System.out.println("j:"+j);
if(j-i+1<minj-mini+1)
{
minj=j;
mini=i;
}
}
return s.substring(mini,minj+1);
}
public static int minaddress(Map<Character, Integer> map)
{
int min=Integer.MAX_VALUE;
for(int value:map.values())
{
if(min>value)
{
min=value;
}
}
return min;
}
}