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 of4
s
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 MapidxMap = 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) { MapidxMap = 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; } }