无重复字符的最长子串

力扣刷这题的时候看到评论区给出一个非常精妙的解答,在此记录一下。

思路

滑动窗口:

  • start:指向子串的起始位置索引
  • end:指向子串的结束位置索引
  • end-start+1:子串长度

窗口有了,接下来需要设计一个缓存结构,用来判断每次end向后移动时,指向的字符是否在子串中出现过。这里使用的是HashMap。key为字符,value为字符在字符串中的索引。

代码

public static int lengthOfLongestSubstring(String s) {
        HashMap<Character, Integer> cache = new HashMap<>();
        //用于记录无重复字符串的最长字串长度
        int max = 0;
        //起始指针
        int start = 0;
        for (int end = 0; end < s.length(); end++) {
            Character ch = s.charAt(end);
            if (cache.containsKey(ch)) {
                start = Math.max(cache.get(ch) + 1,start);
            }
            max = Math.max(end - start + 1, max);
            cache.put(ch, end);
        }
        return max;
    }

疑惑

不理解的地方在于这行代码:

start = Math.max(cache.get(ch) + 1,start)

解析

按照我自己的想法,赋值start的时候有必要和start本身比较么,必定是比他大的吧?为什么不直接跳转呢?

于是普确信的我将这行代码改成:

start = cache.get(ch) + 1 

果然出问题了!
若输入的字符串为"abba",当end指针指向索引为2的'b'时,由于缓存中以及存在了key为'b'的entry,所以start会变成2;进行下一轮循环时,end变为3,ch变为a。 此时,if判断条件成立,start变成字符串中第一个a的索引+1,即0+1=1。如此一来,start就回退了。 出现这个问题的原因是,当我们第一次移动start时,并没有将<'a',0>这个entry移除出HashMap。 所以,后面再出现元素a时,start就会回退到a第一次出现的索引位置。

更一般的,我们每一次移动start时,都没有将任何entry移除出HashMap,而是利用HashMap.put()方法的性质,对缓存中重复字符的索引进行了覆盖。因此,也就不能简单地让start进行跳转。而是需要先进行一个判断:当start>cache.get(ch)时,表示字符ch虽然在字符串中出现过,但已经不在当前维护的字串中了。所以就没有必要对start进行变动。相反的,若start<cache.get(ch),说明字符ch不仅在字符串中出现过,而且在当前维护的字串中也存在,所以需要让start跳转。

本质上,cache中存放的并非是子串中出现过的字符,而是原字符串中出现的字符和其最近一次出现的索引位置。

总结一下,自己还是太菜了。好多精巧的设计需要自己debug才能弄明白。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值