思路
分治思想
先遍歷一遍統計字符串S中每個字母出現的次數,然後再遍歷一遍找到出現次數小於k的一個字母對應的位置(partition),包含S[mid]的子串顯然不可能符合題目要求,所以原問題求S[l,r]字符串對應的答案等價於求S[l,partition-1]和S[partition+1,r];
此外,可以對一些情況進行優化,例如,當l和r所指字符已經不符合題目要求,可以跳過這些字符,以減少計算量。
代碼
未改進代碼
超時
class Solution {
public:
int k;
int longestSubstring(string s, int k) {
this->k = k;
return cnt(s,0,s.size()-1);
}
int cnt(string& s, int l, int r)
{
unordered_map<char, int> ch;
for (int i = l; i <= r; i++)//統計每個字符出現的次數
{
ch[s[i]]++;
}
if (r - l + 1 < k) return 0;//此時該子串size小於k,不存在
//尋找分割位置
//如果在l和r和範圍內遇到不滿足>=k的,即爲分割位置
int partition = l;
while (partition<=r&&ch[s[partition]]>=k)
{
partition++;
}
if (partition > r) return r - l+1;//分割位置不存在,說明此時的串符合要求
//存在分割位置,則取其左右子串兩者的最大值
return max(cnt(s, l, partition - 1), cnt(s, partition + 1, r));
}
};
改進代碼
class Solution {
public:
int k;
int longestSubstring(string s, int k) {
this->k = k;
return cnt(s,0,s.size()-1);
}
int cnt(string& s, int l, int r)
{
unordered_map<char, int> ch;
for (int i = l; i <= r; i++)//統計每個字符出現的次數
{
ch[s[i]]++;
}
//從左到右和從右到左分別跳過字符串中不符合的字符,直到遇到符合的字符位置爲止
while (l<=r&&ch[s[l]]<k)
{
l++;
}
while (l<=r&&ch[s[r]]<k)
{
r--;
}
if (r - l + 1 < k) return 0;//此時該子串size小於k,不存在
//尋找分割位置
//如果在l和r和範圍內遇到不滿足>=k的,即爲分割位置
int partition = l;
while (partition<=r&&ch[s[partition]]>=k)
{
partition++;
}
if (partition >= r) return r - l + 1;//分割位置不存在,說明此時的串符合要求
//存在分割位置,則取其左右子串兩者的最大值
return max(cnt(s, l, partition - 1), cnt(s, partition + 1, r));
}
};