算法通关村——滑动窗口高频问题

滑动窗口之最长子串

无重复字符的最长子串

LeetCode3. 给定一个字符串s,请你找出其中不含有重复字符的最长子串的长度。

示例:

输入:s = “abcabcbb”

输出:3

解释:因为无重复字符的最长子串是"abc",所以其长度为3.

要找最长子串,必然要知道无重复字符串的首和尾,然后再从中确定最长的那个,因此至少两个指针才可以,这就想到了滑动窗口思想。这里介绍一种经典的使用Map的思路。

定义一个K-V形式的map,key表示的是当前正在访问的字符串,vaule是其下标索引值。我们每访问一个新元素,都将其下标更新成对应的索引值。具体过程如下图:

image-20231211102005407

如果是已经出现过的,例如上述示例中的abcabc,当第二次遇到a时,我们就更新left成为第一个b所在的位置,此时可以发现left要移动的位置恰好就是map.get(‘a’) + 1 = 1,将’a’用序列表示,放在一起就是map.get(s.charAt(i)) + 1。其他情况参考图示以此类推。

这里需要考虑一种特殊情况,例如abba,第二次访问b时,更新left,left=map.get(‘b’) + 1 =2

然后继续访问第二个a,此时left=map.get(‘a’)+1=1,也就是left后退了,显然不对。

应该让left在2的基础上继续向前,我们可以和原来的对比一下,将最大的加1就可以了,也就是left=Math.max(left, map.get(s.charAt(i))+1)

完整代码如下:

public int lengthOfLongestSubstring(String s) {
    if (s.length() == 0) return 0;
    HashMap<Character, Integer> map = new HashMap<Character, Integer>();
    int max = 0;
    int left = 0;
    for (int right = 0; right < s.length(); right++) {
        if (map.containsKey(s.charAt(right))) {
            left = Math.max(left, map.get(s.chatAt(right)) + 1);
        }
        map.put(s.charAt(right), right);
        max = Math.max(max, right - left + 1);
    }
    return max;
}

至多包含两个不同字符的最长子串

LeetCode159. 给定一个字符串,找出至多包含两个不同字符的最长子串t,并返回该子串的长度。

示例:

输入:“eceba”

输出:3

解释:t 是"ece",长度为3.

我们仍然使用left和right来锁定一个窗口,然后一边向右移动一边分析。我们用aabbcccd这个序列来看

image-20231211161824078

对于这个题,有两个问题需要解决,一个是怎么判断只有2个元素,另一个是移除的时候怎么知道移除谁,以及移除之后left是什么。

要判断只有2个元素,还是Hash好用,每一个时刻,这个Hashmap包括不超过3个元素。

这里还要考虑到要移除谁,所以我们要设计一下Hash的K-V含义。我们把字符串里的字符都当作键,在窗口中的最右边的字符位置作为值。此时使用下面的代码可以确定删除谁,以及窗口left的新位置:

del_idx = Collections,min(hashmap.values());
left = del_idx + 1;

image-20231211162305711

完整代码如下:

public int lengthOfLongestSubstringTwoDistinct(String s) {
    if(s.length() < 3) {
        return s.length();
    }
    int left = 0, right = 0;
    HashMap<Character, Integer> hashmap = new HashMap<>();
    int maxLen = 2;
    
    while (right < s.length()) {
        if (hashmap.size() < 3) {
            hashmap.put(s.charAt(right), right + 1);
        }
        
        //如果大小达到了3
        if (hashmap.size() == 3) {
            //最左侧要删除的位置
            int delIndex = Collection.min(hashmap.values());
            hashmap.remove(s.charAt(delIndex));
            //窗口left的新位置
            left = delIndex + 1;
        }
        
        maxLen = Math.max(maxLen, right - left);
    }
    return maxLen;
}

如果是至多包含k个不同字符的最长子串,只用将判断hash大小为2改成k就可以了,超过2就是k+1即可。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Molche

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值