CodingBison

Let us go through some of the common problems related to sliding window.

Rabin Karp

Rabin Karp is used for finding a substring of a string. This uses a form of sliding window. Here the pattern is read from left to right. So, if we have pattern length is 3 and string is “23456”, then the hashes would be "234", "345", and "456".

Here are the steps:

Here is the code in Java.

 static void search(String pat, String txt, int Q) {
     int M = pat.length(), N = txt.length(), j, pow = 1, d = 256;
     int pHash= 0, /* hash for pattern */ tHash = 0; /* hash for text */

     // Preprocessing: The value of pow would be "pow(d, M-1) % Q"
     for (int i = 0; i < M-1; i++) pow = (pow*d) % Q;

     // Preprocessing: Calculate the hash value of pattern.
     for (int i = 0; i < M; i++) {
         pHash = (d*pHash + pat.charAt(i)) % Q;
     }

     // Sliding window steps: DAL 
     for (int i = 0; i < N; i++) {
         // Step1 (Delete): Calculate hash value for next window of text: Remove leading digit
         if (i >= M) {
             tHash = (tHash - txt.charAt(i-M)*pow) % Q;
             // We might get negative value of t, converting it to positive
             if (tHash < 0) tHash = (tHash + Q);
         }

         // Step2 (Add): Add the current value.
         tHash = (d * tHash + txt.charAt(i)) % Q;

         // Step3 (Process): Check the hash values of current window of text and pattern.
         // If the hash values match then only check for characters on by one
         if (i >= (M-1)) {
             if (tHash == pHash) {
                 if (pat.equals(txt.substring(i-M+1, i+1)))
                     System.out.println("Pattern found at index " + (i-M+1));
             }
         }
     }
 }

 /* Driver program to test above function */
 public static void main(String[] args) {
     String txt = "GEEKS FOR GEEKS";
     String pat = "GEEK";
     int q = 101; // A prime number
     search(pat, txt, 1 << 20 -1);
 }

The average and best case running time of the Rabin-Karp algorithm is O(n+m), but its worst-case time is O(nm). Worst case of Rabin-Karp algorithm occurs when all characters of pattern and text are same as the hash values of all the substrings of txt[] match with hash value of pat[]. For example pat[] = “AAA” and txt[] = “AAAAAAA”.

Minimum window String (MWS)

Problem: https://leetcode.com/problems/minimum-window-substring/

Given two strings s and t of lengths m and n respectively, we need to return the minimum window substring of s such that every character in t (including duplicates) is included in the window. If there is no such substring, return the empty string "".

Here is the code:

 public String minWindow(String s, String t) {
     int min = Integer.MAX_VALUE, count = 0, lenTarget = t.length(), i = 0;
     String best = "";
     int[] dp = new int[256];

     for (char c: t.toCharArray()) dp[c]++;

     for (int j = 0; j < s.length(); j++) {
         char c = s.charAt(j);
         if (dp[c]-- > 0) count++;
         while (count >= lenTarget && i <= j) {
             if (count == lenTarget) {
                 if (min > (j-i+1)) {
                     min = Math.min(min, j-i+1);
                     best = s.substring(i, j+1);
                 }
             }
             c = s.charAt(i);
             if (++dp[c] > 0) count--;
             i++;
         }
     }
     return best;
 }

Permutation in String

Problem: https://leetcode.com/problems/permutation-in-string/

Given two strings s1 and s2, return true if s2 contains the permutation of s1. The insight is to use Minimum Window Substring (MWS). The solution is similar to that of an anagram.

Example 1: Input: s1 = "ab" s2 = "eidbaooo"/Output: True

Explanation: s2 contains one permutation of s1 ("ba").

Example 2: Input:s1= "ab" s2 = "eidboaoo"/Output: Falsek

Here is the code (in Java):

 public boolean checkInclusion(String s1, String s2) {
     int[] dp = new int[26];
     int[] dp0 = new int[26];

     for (char c: s1.toCharArray()) dp0[c-'a']++;
     int k = s1.length(), count = 0, i = 0;

     for (int j = 0; j < s2.length(); j++) {
         char c = s2.charAt(j);
         dp[c-'a']++;
         if ((dp0[c-'a'] > 0) && (dp[c-'a'] <= dp0[c-'a'])) count++;

         if (j >= k) {
             c = s2.charAt(j-k);
             dp[c-'a']--;
             if (dp[c-'a'] < dp0[c-'a']) count--;
         }

         if (count == k) {
             return true;
         }
     }
     return false;
 }

Find All Anagrams in a String

Problem: https://leetcode.com/problems/find-all-anagrams-in-a-string/

In this problem, we are given a string s and a pattern p and we need to find all occurrences in s such that an anagram of p starts there.

Example1: Input: s: "cbaebabacd" p: "abc"/Output: [0, 6] Example2: Input: s: "abab" p: "ab"/Output: [0, 1, 2]

We can use minimum-window-substring template. It is quite similar to the earlier "Permutation in String" problem. This is actually a fixed window problem since the window that we are exploring has a fixed size.

 public List<Integer> findAnagrams(String s, String p) {
     int[] dp = new int[26];
     int[] dp0 = new int[26];
     List<Integer> ret = new ArrayList<>();
     int k = p.length(), count = 0;

     for (int i = 0; i < p.length(); i++) dp0[p.charAt(i)-'a']++;

     for (int i = 0; i < s.length(); i++) {
         char c = s.charAt(i);
         dp[c-'a']++;
         if ((dp0[c-'a'] > 0) && (dp[c-'a'] <= dp0[c-'a'])) count++;

         if (i >= k) {
             c = s.charAt(i-k);
             dp[c-'a']--;
             if (dp[c-'a'] < dp0[c-'a']) count--;
         }

         if (count == k) ret.add(i-k+1);

     }
     return ret;
 }

Longest Substring with At Least K Repeating Characters

Problem: https://leetcode.com/problems/longest-substring-with-at-least-k-repeating-characters/

In this problem, we need to find the length of the longest substring T of a given string (consists of lowercase letters only) such that every character in T appears no less than k times.

 Example 1: Input: s = "aaabb", k = 3/ Output: 3
     The longest substring is "aaa", as 'a' is repeated 3 times.

 Example 2: Input: s = "ababbc", k = 2/ Output: 5
         The longest substring is "ababb", as 'a' is repeated 2 times and 'b' is repeated 3 times.

For this problem:

 public int longestSubstring(String s, int k) {
     int d = 0;
     for (int numUniqueTarget = 1; numUniqueTarget <= 26; numUniqueTarget++) {
         d = Math.max(d, longestSubstringWithNUniqueChars(s, k, numUniqueTarget));
     }
     return d;
 }

 private int longestSubstringWithNUniqueChars(String s, int k, int numUniqueTarget) {
     int[] map = new int[256];
     int numUnique = 0; // counter 1
     int numAtLeastK = 0; // counter 2
     int i = 0, d = 0;

     for (int j = 0; j < s.length(); j++) {
         if (map[s.charAt(j)]++ == 0) numUnique++; // increment map[c] after this statement
         if (map[s.charAt(j)] == k) numAtLeastK++; // inc end after this statement

         while (numUnique > numUniqueTarget) {
             if (map[s.charAt(i)]-- == k) numAtLeastK--; // decrement map[c] after this statement
             if (map[s.charAt(i)] == 0) numUnique--; // inc begin after this statement
             i++;
         }

         // if we found a string where the number of unique chars equals our target
         // and all those chars are repeated at least K times then update max
         if (numUnique == numUniqueTarget && numUnique == numAtLeastK)
             d = Math.max(j-i+1, d);
     }

     return d;
 }

Maximum Points You Can Obtain from Cards

Problem: https://leetcode.com/problems/maximum-points-you-can-obtain-from-cards/

In this problem, there are several cards arranged in a row, and each card has an associated number of points The points are given in the integer array cardPoints. In one step, you can take one card from the beginning or from the end of the row. You have to take exactly k cards. Given the integer array cardPoints and the integer k, return the maximum score you can obtain.

This is a pretty clever problem. Finding K problems from the two edges is equivalent to the problem of finding (N-K) problems in the middle. So, we can do a sliding window for (N-K) and look for the max of (total_sum - sum(N-K) elements)

 class Solution {
     int ret = 0;

     public int maxScore(int[] a, int k) {
         int tot = 0;
         for (int elem: a) tot += elem;

         int cur = 0, ret = 0;
         k = a.length -k;
         for (int i = 0; i < a.length; i++) {
             cur += a[i];
             if (i < k) {
                 if (i == k-1) {
                     ret = Math.max(ret, tot - cur);
                 }
             } else {
                 cur -= a[i-k];
                 ret = Math.max(ret, tot - cur);
             }
         }

         return ret;
     }
 }




comments powered by Disqus