滑动窗口
双指针思想,i为慢指针,指向当前子串开始位置,j为快指针,指向当前判断的字符
idx[s[j]]记录字符s[j]上次出现的位置
每次迭代记录字符出现位置
当字符s[j]上次出现的位置大于当前子串开始位置i时,比较当前子串长度与目前为止所有子串最大长度,取最大,同时将开始位置设为字符s[j]上次出现位置的下一位
class Solution { public: int lengthOfLongestSubstring(string s) { int res = 0, i = 0, j = 0; vector<int> idx(128, -1); while(j < s.size()) { if(idx[s[j]] >= i) { res = max(res, j - i); i = idx[s[j]] + 1; } idx[s[j++]] = j; } res = max(res, j - i); return res; } };
76. 最小覆盖子串
难度困难
给你一个字符串 S、一个字符串 T,请在字符串 S 里面找出:包含 T 所有字符的最小子串。
示例:
输入: S = "ADOBECODEBANC", T = "ABC" 输出: "BANC"
说明:
- 如果 S 中不存这样的子串,则返回空字符串
""
。 - 如果 S 中存在这样的子串,我们保证它是唯一的答案。
思路:
用哈希表完成,由于匹配数组t可能存在重复值,所以map数值需要是int。
总体算法模拟音乐音量上下浮动,t 的元素个数就是音量高度,t 以外的其他元素都为 0,
遍历 s 的过程中,遇到一个音则将其减少一个音量.(* t 数组以外的音量不可能大于 0)
搜索过程中,指定音量下降计数 cnt 增加,减小到 0 以下不算在内,
当减少总数等于 t 的总数时,所有数就齐了,计算长度。
随后进行回退,如果是 t 以外的音,其增加不改变计数 cnt,直到遇到 t 中的音,
且该值在map中大于0时(区间中 t 的某一个音大于其原始的数量,它map的值可能会小于0),
回退停止,继续向前搜索,重复 步骤 2。
细节:
需要匹配的数组t用unordered_map,查找O(1), 其实如果知道是字母用vector完全够用,但map可拓展性更强一些。
而普通的map是有序二叉树结构,需要二分查找O(log2(n)),不适用于反复进行读取数组的情况。
字符串提取在最后返回时进行,防止过程中反复提取产生消耗。故根据substr(),只要记忆起点和长度就行。
代码
class Solution { public: string minWindow(string s, string t) { unordered_map<char, int> map; for (auto c : t) map[c]++; int left = 0, cnt = 0, maxlen = s.size() + 1, start = left; for (int i = 0; i < s.size(); ++i) { if (--map[s[i]] >= 0) ++cnt; while(cnt == t.size()) { if (maxlen > i - left + 1) { maxlen = i - left + 1; start = left; } if (++map[s[left]] > 0) cnt--; left++; } } return maxlen == s.size() + 1 ? "" : s.substr(start, maxlen); } };