题目

Given a string s and an integer k, return the length of the longest substring of s such that the frequency of each character in this substring is less than or equal to k.

Example 1:

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

Example 2:

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

Constraints:

  • 1 <= s.length <= 10^4
  • s consists of only lowercase English letters.
  • 1 <= k <= 10^5

题意

在指定字符串中找到一个最长的子串,使其包含的每个字母出现的次数都大于等于一个阈值。

思路

求子串问题一般都会想到滑动窗口,但因为本题是要求超过阈值,直接用滑动窗口很难想出一个用来判断该缩短还是伸长窗口的标准。官方解答提供了一个很巧妙的思路:一个子串能拥有的不同字符的种类是受原字符串限制的,所以可以将窗口变动的依据定为“当前子串拥有的不同字符的个数”,这样最多进行26次遍历即可得到答案。


代码实现

Java

  1. class Solution {
  2. public int longestSubstring(String s, int k) {
  3. int ans = 0;
  4. int uniqueCount = 0;
  5. boolean[] exist = new boolean[26];
  6. for (char c : s.toCharArray()) {
  7. if (!exist[c - 'a']) {
  8. exist[c - 'a'] = true;
  9. uniqueCount++;
  10. }
  11. }
  12. for (int ceil = 1; ceil <= uniqueCount; ceil++) {
  13. int unique = 0, satisfied = 0, start = 0, end = 0;
  14. int[] count = new int[26];
  15. while (end < s.length()) {
  16. if (unique <= ceil) {
  17. int index = s.charAt(end) - 'a';
  18. if (count[index] == 0) unique++;
  19. count[index]++;
  20. if (count[index] == k) satisfied++;
  21. end++;
  22. } else {
  23. int index = s.charAt(start) - 'a';
  24. if (count[index] == k) satisfied--;
  25. count[index]--;
  26. if (count[index] == 0) unique--;
  27. start++;
  28. }
  29. if (unique == ceil && unique == satisfied) {
  30. ans = Math.max(ans, end - start);
  31. }
  32. }
  33. }
  34. return ans;
  35. }
  36. }