分析:对于原来的字符串,任何总数没有达到k的字符一定不会出现在满足要求的子串中,所以可以将其作为分界点,将原来的字符串一分为二,分而治之。每一次寻找分界点,耗费o(n),平均需要log(n)次递归,总的复杂度为o(nlog(n)),最差情况下,需要o(n^2).
代码:
class Solution {
public:
int longestSubstring(string s, int k) {
if(s.size() == 0 || k > s.size()) return 0;
if(k == 0) return s.size();
int Map[26] = {0};
for(int i = 0; i < s.size(); i++){
Map[s[i] - 'a']++;
}
int idx =0;
while(idx <s.size() && Map[s[idx] - 'a'] >= k) idx++;
if(idx == s.size()) return s.size();
int left = longestSubstring(s.substr(0 , idx) , k);
while (idx < s.size() && Map[s[idx] - 'a'] < k) ++idx;
int right = longestSubstring(s.substr(idx) , k);
return max(left, right);
}
};
通过截图
这道题最有意思的是关于栈溢出的问题。由于测试样例有一些很长且很特殊的串,并且k的选择也很特殊,导致需要很多次递归调用,导致栈溢出。开始的代码没有while (idx < s.size() && Map[s[idx] - ‘a’] < k) ++idx;这一行,有时说我超时,有时可以通过。通过对这些串的观察,发现这些测试案例导致太多次的递归调用了,使栈溢出,我用visual studio 2017 不能通过测试,报错,用Dev-c++ 可以很快通过。查看了下调用栈,确实栈溢出了,可见visual studio 2017栈内存设置的要小一点。对于递归调用,栈溢出也是一个要考虑的副作用。