滑动窗口

双指针思想,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);
    }
};

 

posted @ 2020-04-29 23:04  刘通1997  阅读(144)  评论(0编辑  收藏  举报