You are given a string s consisting of the characters 'a', 'b', and 'c' and a non-negative integer k. Each minute, you may take either the leftmost character of s, or the rightmost character of s.
Return the minimum number of minutes needed for you to take at least k of each character, or return -1 if it is not possible to take k of each character.
Example 1:
Input: s = "aabaaaacaabc", k = 2
Output: 8
Explanation:
Take three characters from the left of s. You now have two 'a' characters, and one 'b' character.
Take five characters from the right of s. You now have four 'a' characters, two 'b' characters, and two 'c' characters.
A total of 3 + 5 = 8 minutes is needed.
It can be proven that 8 is the minimum number of minutes needed.
Example 2:
Input: s = "a", k = 1
Output: -1
Explanation: It is not possible to take one 'b' or 'c' so return -1.
Constraints:
1 <= s.length <= 105
s consists of only the letters 'a', 'b', and 'c'.
0 <= k <= s.length
每種字元至少取 K 個。
給你一個由字元 'a'、'b'、'c' 組成的字串 s 和一個非負整數 k 。每分鐘,你可以選擇取走 s 最左側 還是 最右側 的那個字元。你必須取走每種字元 至少 k 個,返回需要的 最少 分鐘數;如果無法取到,則返回 -1 。
思路
思路是滑動視窗。這道題要求我們只能從 input 兩側拿字母,那麼 s 的中間部分就是連續的,所以這裡我們可以用滑動視窗。
首先我們統計一下 s 中原來有的 a, b, c 三種字元的個數分別有多少。一個需要排除的 corner case 是如果這三個字母有任何一個字母的出現次數已經不足 k 了,則直接返回 -1。
接著我們可以開始用滑動視窗的模板遍歷 input 陣列了。end 指標往右邊走的時候,start 和 end 中間包含的字母數量是越多的,那麼此時我們可以把 start 和 end 組成的這個視窗理解為留下的字母個數。那麼不包含在這個視窗內的字母就是從 input 字串兩側被移除的字母。此時如果有任何一個字母被移除的次數大於等於 k(反之就是在視窗內的個數小於 k)的時候,我們就要移動 start 指標。
注意這道題我們要找一個儘可能大的視窗,以為題目求的是一個儘可能小的分鐘數。
複雜度
時間O(n)
空間O(1)
程式碼
Java實現
class Solution {
public int takeCharacters(String s, int k) {
int n = s.length();
int[] map = new int[3];
for (char c : s.toCharArray()) {
map[c - 'a']++;
}
for (int i = 0; i < 3; i++) {
if (map[i] < k) {
return -1;
}
}
int res = 0;
int start = 0;
int end = 0;
while (end < s.length()) {
char c1 = s.charAt(end);
map[c1 - 'a']--;
end++;
// 視窗外的字母不足k個,就要移動左指標了
while (map[c1 - 'a'] < k) {
char c2 = s.charAt(start);
map[c2 - 'a']++;
start++;
}
res = Math.max(res, end - start);
}
return n - res;
}
}