Java之滑动窗口详解
目錄
一.滑動窗口
1.什么滑動窗口
2.滑動窗口的三要素
二.找到字符串中所有字母異位詞
1.題目描述
2.問題分析?
3.代碼實現
三.字符串的排列
1.題目描述
2.問題分析
3.代碼實現
四.考試的最大困擾度
1.題目描述
2.問題分析
3.代碼實現
五.替換后的最長重復字符
1.題目描述
2.問題分析
3.代碼實現
六.盡可能使字符串相等
1.題目描述
2.問題分析
3.代碼實現
七.每種字符至少取 K 個
1.題目描述
2.問題分析
3.代碼實現
一.滑動窗口
1.什么滑動窗口
滑動窗口是通過雙指針同向移動而解決的一類問題
經常用于數組或者字符串,求其滿足條件的連續子序列或者子串,將原先需要嵌套循環問題,轉換為單循環問題,降低時間復雜度
主要分為兩大類,一種是長度固定的滑動窗口,一種是長度動態變化的滑動窗口
2.滑動窗口的三要素
我們分析問題主要就是考慮這三要素,尋找滿足題意的條件,使窗口的右端(right)可以向右滑行,滿足條件的時候,使窗口的左端(left)向右滑行,進行收縮,直到對整個數組(或字符串)線性遍歷完成
窗口擴展是尋找可行解
窗口收縮是優化可行解
窗口只能從左至右滑動
注意:長度固定的滑動窗口不需要擴張和收縮,只需要保持固定的長度向右滑動即可
二.找到字符串中所有字母異位詞
1.題目描述
給定兩個字符串?s?和 p,找到?s?中所有?p?的?異位詞?的子串,返回這些子串的起始索引。不考慮答案輸出的順序。
異位詞 指由相同字母重排列形成的字符串(包括相同的字符串)。
力扣:力扣
2.問題分析?
首先我們需要理解異位詞,其實就是含有各個字母數量和一個子串的字母數量相同,那么就可以成為異位,例如(aab)和(baa),他們的長度也是相同的,所以只需要在字符串s中找到長度和p字符串長度相同且各個字母數量相同的字符串即可.這很容易可以想象到滑動窗口,并且長度固定為p的長度的窗口
這里我們采用一個長度為26的字符數組來統計長度為p長度的滑動窗口的字母數量,和p的字符數組進行比較,相同即可加入到list數組中
3.代碼實現
public List<Integer> findAnagrams(String s, String p) {ArrayList<Integer> list = new ArrayList<>();if (p.length() > s.length())return list;int[] sCount = new int[26];int[] pCount = new int[26];for (int i = 0; i < p.length(); ++i) {pCount[p.charAt(i) - 'a']++;}for (int i = 0; i < p.length() - 1; ++i) {sCount[s.charAt(i)-'a']++;}for (int i = 0; i <= s.length() - p.length(); ++i) {sCount[s.charAt(i + p.length() - 1)-'a']++;if (Arrays.equals(sCount, pCount)) {list.add(i);}sCount[s.charAt(i)-'a']--;}return list;}三.字符串的排列
1.題目描述
給你兩個字符串?s1?和?s2 ,寫一個函數來判斷 s2 是否包含 s1?的排列。如果是,返回 true ;否則,返回 false 。
換句話說,s1 的排列之一是 s2 的 子串 。
力扣: 力扣
2.問題分析
這一題和上一題大致相似,自己可以來嘗試一下,排列其實就是異位
3.代碼實現
public boolean checkInclusion(String s1, String s2) {if(s1.length()>s2.length())return false;char[] countS1 = new char[26];char[] countS2 = new char[26];for (int i = 0; i < s1.length(); ++i) {countS1[s1.charAt(i) - 'a']++;countS2[s2.charAt(i) - 'a']++;}if (Arrays.equals(countS1, countS2)) {return true;}for (int i = s1.length(); i < s2.length(); ++i) {countS2[s2.charAt(i - s1.length()) - 'a']--;countS2[s2.charAt(i) - 'a']++;if (Arrays.equals(countS1, countS2)) {return true;}}return false;}四.考試的最大困擾度
1.題目描述
一位老師正在出一場由 n?道判斷題構成的考試,每道題的答案為 true (用 'T' 表示)或者 false (用 'F'?表示)。老師想增加學生對自己做出答案的不確定性,方法是?最大化?有 連續相同?結果的題數。(也就是連續出現 true 或者連續出現 false)。
給你一個字符串?answerKey?,其中?answerKey[i]?是第 i?個問題的正確結果。除此以外,還給你一個整數 k?,表示你能進行以下操作的最多次數:
- 每次操作中,將問題的正確答案改為?'T' 或者?'F'?(也就是將 answerKey[i] 改為?'T'?或者?'F'?)。
請你返回在不超過 k?次操作的情況下,最大?連續 'T'?或者 'F'?的數目。
力扣:力扣
2.問題分析
分析了問題可以知道,這一題包含了字符串,連續T或F字符最大的字眼,因此很容易想到需要使用滑動窗口,因為不確定最大連續字符串的長度,所以這一題的窗口長度是不固定的.問題其實可以分為以下兩種情況:
第一種情況:使用k次機會將遇到的F變成T,在這種情況下使求得連續T的最大數目.
第二種情況:使用k次機會將遇到的T變成F,在這種情況下使求得連續F的最大數目.
最后只需要求得T和F連續的最大數目兩者的最大值即可
分析窗口擴張的情況:(拿求連續T長度最大)因為有k次機會,所以當窗口中F數量小于等于k的時候,這個時候窗口的right向右滑行
分析窗口收縮的情況:當窗口中F的數量大于k的時候,這個時候窗口left進行收縮,直到k的數量小于等于k的時候
滿足條件的窗口大小即為一個符合條件的連續T的長度,只需要尋找滿足條件的窗口的最大值即可.
3.代碼實現
public int maxConsecutiveAnswers(String answerKey, int k) {return Math.max(maxCount(answerKey, k, 'T'), maxCount(answerKey, k, 'F'));}public int maxCount(String answerKey, int k, char c) {int ans = 0;for (int left = 0, right = 0, sum = 0; right < answerKey.length(); ++right) {sum += answerKey.charAt(right) != c ? 1 : 0;while (sum > k) {sum -= answerKey.charAt(left) != c ? 1 : 0;left++;}ans = Math.max(ans, right - left + 1);}return ans;}五.替換后的最長重復字符
1.題目描述
給你一個字符串 s 和一個整數 k 。你可以選擇字符串中的任一字符,并將其更改為任何其他大寫英文字符。該操作最多可執行 k 次。
在執行上述操作后,返回包含相同字母的最長子字符串的長度。
力扣:力扣
2.問題分析
這一題和上一題基本類似,上一題只包含T和F兩種字符,這一題一共26中字符(A--Z),所以要比較26次最大值,求出結果.
分析窗口擴張的情況:當窗口中不等于c字符的數量小于等于k次的時候,窗口右端right向右滑行
分析窗口收縮的情況:當窗口中不等于c字符的數量大于k次的時候,窗口左端left向右滑行,直到窗口中c字符的數量小于等于k次.
3.代碼實現
public int characterReplacement(String s, int k) {int res = 0;HashSet<Character> set = new HashSet<>();for (char c : s.toCharArray()) {set.add(c);}for (Character character : set) {res = Math.max(res, countMax(s, k, character));}return res;}public int countMax(String s, int k, char c) {int res = 0;for (int left = 0, right = 0, cnt = 0; right < s.length(); ++right) {cnt += s.charAt(right) != c ? 1 : 0;while (cnt > k) {cnt -= s.charAt(left) != c ? 1 : 0;left++;}res = Math.max(res, right - left + 1);}return res;}做完這題可以自己去做下:1004. 最大連續1的個數 III: 力扣?? 1493. 刪掉一個元素以后全為 1 的最長子數組:力扣??
六.盡可能使字符串相等
1.題目描述
給你兩個長度相同的字符串,s 和 t。
將 s?中的第?i?個字符變到?t?中的第 i 個字符需要?|s[i] - t[i]|?的開銷(開銷可能為 0),也就是兩個字符的 ASCII 碼值的差的絕對值。
用于變更字符串的最大預算是?maxCost。在轉化字符串時,總開銷應當小于等于該預算,這也意味著字符串的轉化可能是不完全的。
如果你可以將 s 的子字符串轉化為它在 t 中對應的子字符串,則返回可以轉化的最大長度。
如果 s 中沒有子字符串可以轉化成 t 中對應的子字符串,則返回 0。
力扣: 力扣
2.問題分析
這一題雖然和上一題不一樣,但這一題更加簡單,因為很容易想到窗口擴張和收縮的條件
分析窗口擴張的情況:當遍歷到i位置的時候,所需要的預算小于等于maxCost的時候,窗口的右端可以繼續向右滑行
分析窗口收縮的情況:當遍歷到i位置的時候,所需要的預算大于maxCost的時候,窗口的右端不可以繼續向右滑行,這個時候窗口左端left收縮,直到小于maxCost
3.代碼實現
public int equalSubstring(String s, String t, int maxCost) {int res = 0;for (int left = 0, right = 0, sum = 0; right < s.length(); ++right) {sum += Math.abs(t.charAt(right) - s.charAt(right));while (sum > maxCost) {sum -= Math.abs(t.charAt(left) - s.charAt(left));left++;}res = Math.max(res, right - left + 1);}return res;}七.每種字符至少取 K 個
1.題目描述
給你一個由字符 'a'、'b'、'c' 組成的字符串 s 和一個非負整數 k 。每分鐘,你可以選擇取走 s 最左側 還是 最右側 的那個字符。
你必須取走每種字符 至少 k 個,返回需要的 最少 分鐘數;如果無法取到,則返回 -1 。
力扣:力扣
2.問題分析
正難則反,我們不妨換一個角度考慮一下問題,問題是我們每次從左端或右端取走字符,最終使取走各k個字符'a','b','c',那么我們不妨這樣考慮:取走k個字符'a','b','c',字符串中還剩下多少個字符'a','b','c',求出長度最大的含有這樣的子串,最終最小的分鐘等于字符串的長度減去這個子串
設子串中要剩余至多cntA個'a',cntB個'b',cntC個'c'
分析窗口擴張的情況:當子串(滑動窗口)中所有字母的數量小于等于所需的數量(cntA,cntB,cntC)時候,窗口的right端向右滑行
分析窗口收縮的情況:當子串(滑動窗口)中任一個字母的數量大于所需的數量(cntA,cntB,cntC)時候,窗口的left端向左滑行,直至不符合條件
每次需要收集滿足條件的窗口的長度,尋找到最大長度的窗口,最終的答案就是字符串的長度減去滑動窗口長度的最大值
3.代碼實現
public int takeCharacters(String s, int k) {int left = 0, right = 0, length = s.length();char[] arr = s.toCharArray();int max = 0;int[] cnt = new int[3];//統計a,b,c的數量for (int i = 0; i < length; ++i) {cnt[arr[i] - 'a']++;}int cntA = cnt[0] - k, cntB = cnt[1] - k, cntC = cnt[2] - k;//分別為a,b,c可以剩下的最大數量if (cntA == 0 && cntB == 0 && cntC == 0)//此時要全部取走return length;if (cntA < 0 || cntB < 0 || cntC < 0)//剩下的數量為負的時候,說明a,b,c的數量不足k個return -1;cnt = new int[3];//每次循環統計剩下的a,b,c的數量while (right < length) {cnt[arr[right] - 'a']++;while (cnt[0] > cntA || cnt[1] > cntB || cnt[2] > cntC) {cnt[arr[left] - 'a']--;left++;//當剩下的字符串過長而不滿足條件的時候,滑動窗口左端向右移}max = Math.max(max, right - left+1);right++;//窗口的左端向右移}return length - max;}做完這題,建議做一下1658. 將 x 減到 0 的最小操作數:力扣
總結
以上是生活随笔為你收集整理的Java之滑动窗口详解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 关于Vue-Cli proxy 不生效的
- 下一篇: java美元兑换,(Java实现) 美元