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.
这道题给了我们一个字符串s和一个正整数k,让我们求一个最大子字符串并且每个字符必须至少出现k次。作为LeetCode第三次编程比赛的压轴题目,博主再一次没有做出来,虽然难度标识只是Medium。后来在网上膜拜学习了大神们的解法,发现我当时的没做出来的原因主要是卡在了如何快速的判断某一个字符串是否所有的元素都已经满足了至少出现k次这个条件,虽然我也用哈希表建立了字符和其出现次数之间的映射,但是如果每一次都要遍历哈希表中的所有字符看其出现次数是否大于k,未免有些不高效。而用mask就很好的解决了这个问题,由于字母只有26个,而整型mask有32位,足够用了,每一位代表一个字母,如果为1,表示该字母不够k次,如果为0就表示已经出现了k次,这种思路真是太聪明了,隐约记得这种用法在之前的题目中也用过,但是博主并不能举一反三(沮丧脸:(),还得继续努力啊。我们遍历字符串,对于每一个字符,我们都将其视为起点,然后遍历到末尾,我们增加哈希表中字母的出现次数,如果其小于k,我们将mask的对应位改为1,如果大于等于k,将mask对应位改为0。然后看mask是否为0,是的话就更新res结果,然后把当前满足要求的子字符串的起始位置j保存到max_idx中,等内层循环结束后,将外层循环变量i赋值为max_idx+1,继续循环直至结束,参见代码如下:
解法一:
class Solution { public: int longestSubstring(string s, int k) { int res = 0, i = 0, n = s.size(); while (i + k <= n) { int m[26] = {0}, mask = 0, max_idx = i; for (int j = i; j < n; ++j) { int t = s[j] - 'a'; ++m[t]; if (m[t] < k) mask |= (1 << t); else mask &= (~(1 << t)); if (mask == 0) { res = max(res, j - i + 1); max_idx = j; } } i = max_idx + 1; } return res; } };
下面这种写法是上面的解法的递归写法,看起来简洁了不少,但是个人感觉比较难想,参见代码如下:
解法二:
class Solution { public: int longestSubstring(string s, int k) { int n = s.size(), max_idx = 0, res = 0; int m[128] = {0}; bool ok = true; for (char c : s) ++m[c]; for (int i = 0; i < n; ++i) { if (m[s[i]] < k) { res = max(res, longestSubstring(s.substr(max_idx, i - max_idx), k)); ok = false; max_idx = i + 1; } } return ok ? n : max(res, longestSubstring(s.substr(max_idx, n - max_idx), k)); } };
本文转自博客园Grandyang的博客,原文链接:至少有K个重复字符的最长子字符串[LeetCode] Longest Substring with At Least K Repeating Characters ,如需转载请自行联系原博主。