十六 滑動窗口專題

滑動窗口首先按照類型分溅漾,可以分為定長窗口和變長窗口吃环。
定長窗口也就是說題目給出來的時候吠撮,你知道這個窗口的長度是固定的孝冒,一般會要求你返回每個這樣的窗口柬姚。我們只需要處理I,J 2根指針同時向后移一位。然后每次的位移過程里做的事是需要技巧的庄涡。

定長窗口典型題目有

Given an integer array A and a sliding window of size K, find the maximum value of each window as it slides from left to right.

Assumptions

The given array is not null and is not empty

K >= 1, K <= A.length

Examples

A = {1, 2, 3, 2, 4, 2, 1}, K = 3, the windows are {{1,2,3}, {2,3,2}, {3,2,4}, {2,4,2}, {4,2,1}},

and the maximum values of each K-sized sliding window are [3, 3, 4, 4, 4]

或者
Find all occurrence of anagrams of a given string s in a given string l. Return the list of starting indices.

Assumptions

s is not null or empty.
l is not null.
Examples

l = "abcbac", s = "ab", return [0, 3] since the substring with length 2 starting from index 0/3 are all anagrams of "ab" ("ab", "ba").

上面第一個題目的技巧是維護一個單調(diào)DEQUE量承,我們需要思考的是一個窗口里的最大值取決于什么,有哪些元素對最大值是完全沒有貢獻的啼染。我們發(fā)現(xiàn)宴合,一個位置靠前并且比這個窗口里后面的值還小的那個數(shù) 是不可能對之后窗口的最大值產(chǎn)生價值。所以我們可以之前把它丟掉迹鹅。根據(jù)這個原則卦洽。我們DEQUE的最左端是該窗口的最大值。同時DEQUE維持了單調(diào)遞減的特性斜棚。后面繼續(xù)保持這個特性即可阀蒂。

public List<Integer> maxWindows(int[] array, int k) {
    // Write your solution here
    Deque<Integer> dq = new ArrayDeque<>();
    if (k > array.length) k = array.length;
    for (int i = 0; i < k; i++) {
      while (!dq.isEmpty() && array[i] > dq.peekLast()) dq.pollLast();
      dq.offerLast(array[i]);
    }
    List<Integer> res = new ArrayList<>();
    res.add(dq.peekFirst());
    for (int i = k; i < array.length; i++) {
      if (array[i - k] == dq.peekFirst()) dq.pollFirst();
      while (!dq.isEmpty() && array[i] > dq.peekLast()) dq.pollLast();
      dq.offerLast(array[i]);
      res.add(dq.peekFirst());
    }
    return res;
  }

第二道題,就是要看什么時候需要去更新CNT變量弟蚀,只有當(dāng)S里的字符的計數(shù)是正的時候蚤霞,刪除的時候需要CNT--。是》=0的時候义钉,增加的時候需要CNT++昧绣。 因為無用的字符,前面沒有初始化捶闸。所以增加的時候那時的值必然為負數(shù)夜畴。

public List<Integer> allAnagrams(String sh, String lo) {
    int i = 0;
    int[] map = new int[256];
    for (char c : sh.toCharArray()) map[c]++;
    int cnt = sh.length();
    List<Integer> res = new ArrayList<>();
    if (lo.length() < sh.length()) return res;
    int j = 0;
    while (j < sh.length()) {
     if (map[lo.charAt(j++)]-- > 0){
       cnt--;
     }
    }
    if (cnt == 0) res.add(i);
    while (j < lo.length()) {
      if (map[lo.charAt(i++)]++ >= 0){
       cnt++;
      }
      if (map[lo.charAt(j++)]-- > 0) {
        cnt--;
      }
      if (cnt == 0) res.add(i);
    }
    
    return res;
  }

其他定長窗口
Given a string containing only 'A', 'B', 'C', 'D', return the number of occurrences of substring which has length 4 and includes all of the characters from 'A', 'B', 'C', 'D' with in descending sorted order.

Assumptions:

The given string is not null and has length of >= 4.
In the output, if two substrings have the same frequency, then they should be sorted by the their natural order.
Examples:

“ABCDABCDD”, --> {"ABCD" : 2, "BCDA" : 1, "CDAB" : 1, "DABC" : 1}

/**
 * public class Frequency {
 *   public String str;
 *   public int freq;
 *   public Frequency(String str, int freq) {
 *     this.str = str;
 *     this.freq = freq;
 *   }
 * }
 */
public class Solution {
  public List<Frequency> frequency(String input) {
    Map<String,Integer> map = new HashMap<>();
    int[] m = new int[4];
    int i = 0;
    int cnt = 0;
    char[] cs = input.toCharArray();
    for (; i < 4; i++) {
      if(m[cs[i] - 'A']++ == 0) cnt++;
    }
    for (; i <= cs.length; i++) {
      if (cnt == 4) {
        String key = input.substring(i - 4, i);
        map.putIfAbsent(key,0);
        map.put(key, map.get(key) + 1);
      }
      if (i == cs.length) break;
      if(--m[cs[i - 4] - 'A'] == 0) cnt--;
      if(m[cs[i] - 'A']++ == 0) cnt++;
    }
    List<Frequency> res = new ArrayList<Frequency>();
    for (String key : map.keySet()) {
      res.add(new Frequency(key, map.get(key)));
    }
    Collections.sort(res, new Comparator<Frequency> () {
      public int compare(Frequency a, Frequency b) {
        if (a.freq == b.freq) return a.str.compareTo(b.str);
        return b.freq - a.freq;
      }
    });
              
    return res;
  }
}

下面我們來看變長的窗口。

變長窗口最讓人頭疼的問題是很容易删壮,移著移著指針就混亂了贪绘,一跑代碼不是有錯誤,就是INDEX越界央碟。所以基于這個問題税灌,我設(shè)計了一套針對滑動窗口的不變量來避免寫代碼的時候發(fā)生想不清楚而引入BUG或者死循環(huán)指針越界的問題。

變長窗口會有2類 一般會讓你找最長的窗口亿虽,或者最短的窗口菱涤。所以道題滑動變長窗口題,可以僅僅改一個單詞洛勉,就讓題目變化狸窘。
比如:
Given a string, return the longest contiguous substring that contains exactly k type of characters.

Return null if there does not exist such substring.

Assumptions:

The given string is not null.
k >= 0.
Examples:

input = "aabcc", k = 3, output = "aabcc".
input = "aabcccc", k = 2, output = "bcccc".

變化:
Given a string, return the shortest contiguous substring that contains exactly k type of characters.

Return an empty string if there does not exist such substring.

Assumptions:

The given string is not null.
k >= 0.
Examples:

input = "aabcc", k = 3, output = "abc".
input = "aabbbcccc", k = 3, output = "abbbc".
input = "aabcc", k = 4, output = "".

如果求最長,也就是在窗口滿足條件的時候坯认,移動后面一個(J)指針翻擒。
如果求最短氓涣,則相反,當(dāng)窗口滿足條件時陋气,移動前一個(I)指針劳吠。

根據(jù)上述思想,我們要思考I,J的物理意義
我個人一般喜歡定義窗口區(qū)間為前閉巩趁,后開
這樣給I,J 賦初始值的時候可以為0,0

那么循環(huán)退出一般是J 》 ARRAY.LENGTH的時候
所以我一般會用如下框架解決滑動窗口痒玩。
先看找最小窗口

        int min = Integer.MAX_VALUE;
        int i = 0, j = 0;
        while (j <= array.length) {
            if (滿足條件) {
                min = Math.min(min, j - i);
                updateState(i++)
            } else {
                if (j == array.length) break;
                updateState(j++)
            }
        }
        return min;

最長窗口

        int max= 0;
        int i = 0, j = 0;
        while (j <= array.length) {
            if (窗口需要增大) {
                if (滿足條件)
                    max= Math.max(max, j - i);
                if (j == array.length) break;
                updateState(j++)
            } else {
                updateState(i++)
            }
        }
        return max;

根據(jù)這2個設(shè)計思路,我們可以很輕松的解決上面2個問題议慰。

public String shortest(String input, int k) {
    char[] cs = input.toCharArray();
    int min = cs.length;
    int s = 0, e = 0;
    int i = 0, j = 0;
    int[] map = new int[256];
    if (k == 0) return "";
    while (j <= cs.length) {
      if (k == 0) {
        if (j - i < min) {
          min = j - i;
          s = i;
          e = j;
        }
        if (++map[cs[i++]] == 0) k++;
      } else {
        if (j == cs.length) break;
        if (map[cs[j++]]-- == 0) k--;
      }
    }
    return input.substring(s,e);
  }

最長

public String longest(String input, int k) {
    char[] cs = input.toCharArray();
    int max = 0;
    int s = 0, e = 0;
    int i = 0, j = 0;
    int[] map = new int[256];
    if (k == 0) return "";
    while (j <= cs.length) {
      if (k >= 0) {
        if (k == 0 && j - i > max) {
          max = j - i;
          s = i;
          e = j;
        }
        if (j == cs.length) break;
        
        if (map[cs[j++]]-- == 0) k--;
        
      } else {
        if (++map[cs[i++]] == 0) k++;
      }
    }
    return s == e ? null : input.substring(s,e);
  }

我們再來看幾道別的題蠢古,是不是都差不多了。

Minimum Window Substring

Given a string S and a string T, find the minimum window in S which will contain all the characters in T

Input: S = “ADOBECODEBANC”

      T = “ABC”

Output: “BANC”

public String minWindow(String source, String target) {
    int[] map = new int[256];
    for (char c : target.toCharArray()) {
      map[c]++;
    }
    int i = 0, j = 0;
    char[] cs = source.toCharArray();
    int cnt = target.length();
    if (cnt == 0) return target;
    int min = source.length();
    int s = 0, e = 0;
    while (j <= cs.length) {
      if (cnt == 0) {
        if (j - i < min) {
          min = j - i;
          s = i;
          e = j;
        }
        if (map[cs[i++]]++ == 0) cnt++;
      } else {
        if (j == cs.length) break;
        if (map[cs[j++]]-- > 0) cnt--;
      }
    }
    return source.substring(s,e);
  }

Smallest Substring Containing All Characters Of Another String

Given two strings s1 and s2, find the shortest substring in s1 containing all characters in s2.

If there does not exist such substring in s1, return an empty string.

Assumptions:

s1 and s2 are not null or empty.
Examples:

s1= “The given test strings”

s2: “itsst”

Output string: “st stri”

public String smallest(String s1, String s2) {
    int[] map = new int[256];
    for (char c : s2.toCharArray()) map[c]++;
    int cnt = s2.length();
    int i = 0, j = 0;
    char[] cs = s1.toCharArray();
    int min = Integer.MAX_VALUE, s = 0, e = 0;
    while (j <= cs.length) {
      if (cnt == 0) {
        if (j - i < min) {
          min = j - i;
          s = i;
          e = j;
        }
        if (++map[cs[i++]] > 0) cnt++;
      } else{
        if (j == cs.length) break;
        if (map[cs[j++]]-- > 0) cnt--;
      }
    }
    return s1.substring(s,e);
  }

Minimum Size Subarray Sum

Given an array of n positive integers and a positive integer s, find the minimal length of a contiguous subarray of which the sum ≥ s. If there isn't one, return 0 instead.

For example, given the array [2,3,1,2,4,3] and s = 7,
the subarray [4,3] has the minimal length under the problem constraint.

public int minSubArrayLen(int num, int[] nums) {
    int i = 0, j = 0, min = Integer.MAX_VALUE;
    if (num <= 0) return 0;
    while (j <= nums.length) {
      if (num <= 0) {
        min = Math.min(min, j - i);
        num += nums[i++];
      } else {
        if (j == nums.length) break;
        num -= nums[j++];
      }
    }
    return min == Integer.MAX_VALUE ? 0 : min;
  }

最長的變長窗口

Longest Substring with At Most Two Distinct Characters

Given a string, find the the longest substring T that contains at most 2 distinct characters.

For example, Given s = “eceba”, T is "ece"

public int lengthOfLongestSubstringTwoDistinct(String input) {
        char[] cs = input.toCharArray();
        int i = 0, j = 0;
        int k = 2;
        int[] map = new int[256];
        int max = 0;
        while (j <= cs.length) {
            if (k >= 0) {
              max = Math.max(max, j - i);
              if (j == cs.length) break;
              if (map[cs[j++]]++ == 0) k--;
            } else {
              if (--map[cs[i++]] == 0) k++;
            } 
        }
        return max;
  }
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末别凹,一起剝皮案震驚了整個濱河市草讶,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌炉菲,老刑警劉巖堕战,帶你破解...
    沈念sama閱讀 217,185評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異拍霜,居然都是意外死亡嘱丢,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評論 3 393
  • 文/潘曉璐 我一進店門祠饺,熙熙樓的掌柜王于貴愁眉苦臉地迎上來越驻,“玉大人,你說我怎么就攤上這事道偷》ヌ福” “怎么了?”我有些...
    開封第一講書人閱讀 163,524評論 0 353
  • 文/不壞的土叔 我叫張陵试疙,是天一觀的道長。 經(jīng)常有香客問我抠蚣,道長祝旷,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,339評論 1 293
  • 正文 為了忘掉前任嘶窄,我火速辦了婚禮怀跛,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘柄冲。我一直安慰自己吻谋,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,387評論 6 391
  • 文/花漫 我一把揭開白布现横。 她就那樣靜靜地躺著漓拾,像睡著了一般阁最。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上骇两,一...
    開封第一講書人閱讀 51,287評論 1 301
  • 那天速种,我揣著相機與錄音,去河邊找鬼低千。 笑死配阵,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的示血。 我是一名探鬼主播棋傍,決...
    沈念sama閱讀 40,130評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼难审!你這毒婦竟也來了瘫拣?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,985評論 0 275
  • 序言:老撾萬榮一對情侶失蹤剔宪,失蹤者是張志新(化名)和其女友劉穎拂铡,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體葱绒,經(jīng)...
    沈念sama閱讀 45,420評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡感帅,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,617評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了地淀。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片失球。...
    茶點故事閱讀 39,779評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖帮毁,靈堂內(nèi)的尸體忽然破棺而出实苞,到底是詐尸還是另有隱情,我是刑警寧澤烈疚,帶...
    沈念sama閱讀 35,477評論 5 345
  • 正文 年R本政府宣布黔牵,位于F島的核電站,受9級特大地震影響爷肝,放射性物質(zhì)發(fā)生泄漏猾浦。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,088評論 3 328
  • 文/蒙蒙 一灯抛、第九天 我趴在偏房一處隱蔽的房頂上張望金赦。 院中可真熱鬧,春花似錦对嚼、人聲如沸夹抗。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽漠烧。三九已至杏愤,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間沽甥,已是汗流浹背声邦。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留摆舟,地道東北人亥曹。 一個月前我還...
    沈念sama閱讀 47,876評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像恨诱,于是被迫代替她去往敵國和親媳瞪。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,700評論 2 354

推薦閱讀更多精彩內(nèi)容

  • rljs by sennchi Timeline of History Part One The Cognitiv...
    sennchi閱讀 7,324評論 0 10
  • Lua 5.1 參考手冊 by Roberto Ierusalimschy, Luiz Henrique de F...
    蘇黎九歌閱讀 13,791評論 0 38
  • pyspark.sql模塊 模塊上下文 Spark SQL和DataFrames的重要類: pyspark.sql...
    mpro閱讀 9,451評論 0 13
  • 保持樂觀的心態(tài)吧!這句話不僅僅是寫給這篇文章的讀者厕鹃,更重要的是想要寫給我自己兢仰! 能夠長久的保持樂觀實在是一件很厲害...
    櫛風(fēng)沐雨1閱讀 607評論 0 1
  • 感覺中間有點空,所以又加了兩片葉子剂碴。 今天臨摹了一副葡萄風(fēng)信子把将。
    晨月悅閱讀 131評論 0 0