[LeetCode 1234] Replace the Substring for Balanced String

You are given a string containing only 4 kinds of characters 'Q', 'W', 'E' and 'R'.

A string is said to be balanced if each of its characters appears n/4 times where n is the length of the string.

Return the minimum length of the substring that can be replaced with any other string of the same length to make the original string s balanced.

Return 0 if the string is already balanced.

 

Example 1:

Input: s = "QWER"
Output: 0
Explanation: s is already balanced.

Example 2:

Input: s = "QQWE"
Output: 1
Explanation: We need to replace a 'Q' to 'R', so that "RQWE" (or "QRWE") is balanced.

Example 3:

Input: s = "QQQW"
Output: 2
Explanation: We can replace the first "QQ" to "ER". 

Example 4:

Input: s = "QQQQ"
Output: 3
Explanation: We can replace the last 3 'Q' to make s = "QWER".

 

Constraints:

  • 1 <= s.length <= 10^5
  • s.length is a multiple of 4
  • contains only 'Q''W''E' and 'R'.

 

Solution 1. Binary Search on the final answer. O(N * log N) runtime, N is the input string's length

1. Do a linear scan to get all 4 letters' counts. Subtract each letter's count by s.length() / 4. This represents for each letter, how many letters are redundant (count - s.length() / 4 > 0) or missing(count - s.length() / 4 < 0). 

2. The minimum window must be in range [0, s.length()]. Binary search on this range, for each window length WL that needs to be checked, do the following.

From left to right, check each winodw of length WL can make s balanced. If true, search on the left half, including WL; If false, search on the right half, excluding WL. This is correct because if a winow of length WL can not make s balanced, then all windows of smaller length can not make s balanced either. The answer must be in the right half of the search range. 

Each check on a certain window length takes O(N) time. Each time a character slides out, we add 1 to the according count because there is 1 fewer character to reduce the difference against s.length() / 4. Each time a character slides in, we subtract the according count by 1 because there is 1 more character to reduce the difference against s.length() / 4.

 

When all 4 difference count are <= 0, the current window size makes s balanced. 

 Proof:  As long as there is at least 1 difference count of letter c that is > 0, we know that the maximum c we can get rid of inside the current window is not enough. Because the total characters' count is fixed, if we can get rid of all redundant letters inside a window, we can also add all required missing letters to make s balanced. The key here is that for a given window and letter counts that is > s.length() / 4, we can only get rid of at most K redundant letter for each count, K is the count of such a letter inside the current sliding window.

 

class Solution {
    private Map idxMap = new HashMap<>();
    public int balancedString(String s) {
        idxMap.put('Q', 0); idxMap.put('W', 1); idxMap.put('E', 2); idxMap.put('R', 3);
        int[] cnt = new int[4];
        for(int i = 0; i < s.length(); i++) {
            cnt[idxMap.get(s.charAt(i))]++;
        }
        for(int i = 0; i < cnt.length; i++) {
            cnt[i] -= (s.length() / 4);
        }
        int left = 0, right = s.length();
        while(left < right - 1) {
            int mid = left + (right - left) / 2;
            int[] cntCopy = Arrays.copyOf(cnt, cnt.length);
            if(check(s, cntCopy, mid)) {
                right = mid;
            }
            else {
                left = mid + 1;
            }
        }
        int[] cntCopy = Arrays.copyOf(cnt, cnt.length);
        if(check(s, cntCopy, left)) {
            return left;
        }
        return right;
    }
    private boolean check(String s, int[] cnt, int window) {
        for(int i = 0; i < window; i++) {
            cnt[idxMap.get(s.charAt(i))]--;
        }
        if(cnt[0] <= 0 && cnt[1] <= 0 && cnt[2] <= 0 && cnt[3] <= 0) {
            return true;
        }
        for(int i = window; i < s.length(); i++) {
            cnt[idxMap.get(s.charAt(i - window))]++;
            cnt[idxMap.get(s.charAt(i))]--;
            if(cnt[0] <= 0 && cnt[1] <= 0 && cnt[2] <= 0 && cnt[3] <= 0) {
                return true;
            }          
        }
        return false;
    }
}

 

Solution 2. Sliding window + two pointers, O(N) runtime.

Similarly with solution 1, except instead of a binary search on the final answer, use a left and right pointers to represent the minimum length window that can make s balanced. 

1. as long as s is not balanced, keep moving r to the right;

2. as soons as s is balanced, keep moving l to the right until s is not balanced anymore.

3. update res as Math.min(res, r - l + 1) then repeat these 3 steps until r is out of bound.

 

class Solution {
    public int balancedString(String s) {
        Map idxMap = new HashMap<>();
        idxMap.put('Q', 0); idxMap.put('W', 1); idxMap.put('E', 2); idxMap.put('R', 3);
        int[] cnt = new int[4];
        Arrays.fill(cnt, -s.length() / 4);
        
        for(int i = 0; i < s.length(); i++) {
            cnt[idxMap.get(s.charAt(i))]++;
        }
        
        if(cnt[0] == 0 && cnt[1] == 0 && cnt[2] == 0 && cnt[3] == 0) {
            return 0;
        }
        
        int l = 0, r = 0, res = s.length();
        while(r < s.length()) {
            while(r < s.length() && (cnt[0] > 0 || cnt[1] > 0 || cnt[2] > 0 || cnt[3] > 0)) {
                cnt[idxMap.get(s.charAt(r))]--;
                r++;
            }
            //no need to check if l is out of bound, because we already exclude the answer 0 case;
            //after l is incremented to s.length(), cnt[0] <= 0 && cnt[1] <= 0 && cnt[2] <= 0 && cnt[3] <= 0
            //is always false.
            while(l <= r && cnt[0] <= 0 && cnt[1] <= 0 && cnt[2] <= 0 && cnt[3] <= 0) {
                cnt[idxMap.get(s.charAt(l))]++;
                l++;
            }
            res = Math.min(res, r - l + 1);
        }
        return res;
    }
}

 

你可能感兴趣的:([LeetCode 1234] Replace the Substring for Balanced String)