出问题的地方在于 sum == target 条件的 if 分支,当给 res 加入一次结果后,lo 和 hi 不应该改变 1 的同时,还应该跳过所有重复的元素:
所以,可以对双指针的 while 循环做出如下修改:
vector<vector<int>> twoSumTarget(vector<int>& nums, int target) {
// nums 数组必须有序
// sort(nums.begin(), nums.end()); // 如果无序,则排序
int lo = 0, hi = nums.size() - 1;
vector<vector<int>> res;
while (lo < hi) {
int sum = nums[lo] + nums[hi];
int left = nums[lo], right = nums[hi];
if (sum < target) {
while (lo < hi && nums[lo] == left) lo++;
} else if (sum > target) {
while (lo < hi && nums[hi] == right) hi--;
} else {
res.push_back({left, right});
while (lo < hi && nums[lo] == left) lo++;
while (lo < hi && nums[hi] == right) hi--;
}
}
return res;
}
这样,一个通用化的 twoSum 函数就写出来了。这个函数的时间复杂度非常容易看出来,双指针操作的部分虽然有那么多 while 循环,但是时间复杂度还是 O(N),而排序的时间复杂度是 O(NlogN),所以这个函数的时间复杂度是 O(NlogN)。
/* 注意:调用这个函数之前一定要先给 nums 排序 */
vector<vector<int>> nSumTarget(
vector<int>& nums, int n, int start, int target) {
int sz = nums.size();
vector<vector<int>> res;
// 至少是 2Sum,且数组大小不应该小于 n
if (n < 2 || sz < n) return res;
// 2Sum 是 base case
if (n == 2) {
// 双指针那一套操作
int lo = start, hi = sz - 1;
while (lo < hi) {
int sum = nums[lo] + nums[hi];
int left = nums[lo], right = nums[hi];
if (sum < target) {
while (lo < hi && nums[lo] == left) lo++;
} else if (sum > target) {
while (lo < hi && nums[hi] == right) hi--;
} else {
res.push_back({left, right});
while (lo < hi && nums[lo] == left) lo++;
while (lo < hi && nums[hi] == right) hi--;
}
}
} else {
// n > 2 时,递归计算 (n-1)Sum 的结果
for (int i = start; i < sz; i++) {
vector<vector<int>>
sub = nSumTarget(nums, n - 1, i + 1, target - nums[i]);
for (vector<int>& arr : sub) {
// (n-1)Sum 加上 nums[i] 就是 nSum
arr.push_back(nums[i]);
res.push_back(arr);
}
while (i < sz - 1 && nums[i] == nums[i + 1]) i++;
}
}
return res;
}
给定一个字符串 s 和一个非空字符串 p,找到 s 中所有是 p 的字母异位词的子串,返回这些子串的起始索引。字符串只包含小写英文字母,并且字符串 s 和 p 的长度都不超过 20100。
说明:字母异位词指字母相同,但排列不同的字符串。 不考虑答案输出的顺序。
vector<int> findAnagrams(string s, string p) {
std::unordered_map<char, int> need, window;
std::vector<int> result;
for(char c : p)
need[c]++;
int left = 0, right = 0;
int valid = 0;
while(right < s.size()){
char c = s[right];
right++;
if(need.count(c)){
window[c]++;
if(need[c] == window[c])
valid++;
}
while(right - left >= p.size()){
if(valid == need.size())
result.push_back(left);
char d = s[left];
left++;
if(need.count(d)){
if(need[d] == window[d])
valid--;
window[d]--;
}
}
}
return result;
}
给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
int lengthOfLongestSubstring(string s) {
int result = 0;
int len = s.size();
if(len == 0 || len == 1)
return len;
unordered_map<char, int> window;
int left = 0, right = 0;
while(right < len){
char c = s[right];
right++;
window[c]++;
if(window[c] > 1){
result = max(result, right - left - 1);
while(s[left] != c){
window[s[left]]--;
left++;
}
window[s[left]]--;
left++;
}
}
return max(result, right - left);
}
int lengthOfLongestSubstring(string s) {
int result = 0;
int len = s.size();
if(len == 0 || len == 1)
return len;
int start = 0, current = 1;
while(current < len){
for(int i = current - 1; i >= start; i--){
if(s[i] == s[current]){
start = i + 1;
break;
}
}
result = result < (current - start + 1) ? (current - start + 1) : result;
current++;
}
return result;
}
给定一个由若干 0 和 1 组成的数组 A,我们最多可以将 K 个值从 0 变成 1 (0 <= K <= A.length)。返回仅包含 1 的最长(连续)子数组的长度。
int longestOnes(vector<int>& A, int K) {
if(K == A.size())
return K;
int left = 0, right = 0;
int result = 0, window = 0;
while(right < A.size()){
int i = A[right];
right++;
if(i == 1 || K > 0) {
window++;
if(i == 0)
K--;
}else{
result = std::max(result, window);
window++;
K--;
while(K < 0){
int j = A[left];
left++;
window--;
if(j == 0){
K++;
}
}
}
}
return std::max(result, window);
}
给定一个字符串 s ,找出 至多 包含 k 个不同字符的最长子串 T。
int lengthOfLongestSubstringKDistinct(std::string s, int k){
if(s.size() == 0 || k <= 0)
return 0;
unordered_map<char, int > m;
int l = 0, r = 0; // 滑动窗口左右指针
int maxLen = 1; // 最大长度
int count = 0;
while(r < s.size()){
if (m[s[r]] == 0)
count ++;
m[s[r]] += 1;
r++;
// 左指针移动减小
while (count > k){
if(m[s[l]] == 1)
count--;
m[s[l]] -= 1;
l++;
}
maxLen = max(maxLen, r - l);
}
return maxLen;
}