滑動窗口的真實奧祕
一個窗口在數組上緩緩滑動
奧妙的是下一個窗口可以借鑑上一個窗口的值
字符串常用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)
思路:
依然是滑動窗口
用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;
}
}