Leetcode 3-无重复字符的最长子串 暴力解法转变到快速解法的详解

题目详情

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

示例 1:

输入: “abcabcbb”
输出: 3
解释: 因为无重复字符的最长子串是 “abc”,所以其长度为 3。
示例 2:

输入: “bbbbb”
输出: 1
解释: 因为无重复字符的最长子串是 “b”,所以其长度为 1。
示例 3:

输入: “pwwkew”
输出: 3
解释: 因为无重复字符的最长子串是 “wke”,所以其长度为 3。
请注意,你的答案必须是 子串 的长度,“pwke” 是一个子序列,不是子串。

暴力解法

面对一道题,一上来还是要稍微想一下暴力解法的,不然直接改进,很容易出问题。在有暴力解法的基础上,进行改进,去重复,取巧,很快,bug也很少。

先来将暴力解法,很容易想到啦,遍历字符串,在每个点,都往后一直遍历,把每个字符加入set,直到遇到重复的字符为止。
代码如下:

class Solution {
    public int lengthOfLongestSubstring(String s) {
        //首先暴力解法,每次用set与后面比较
        Set<Character> set = new HashSet<>();
        int maxLen =0;
        for(int i=0; i<s.length(); i++){
            int len =0;
            for(int j = i; j<s.length(); j++){
                char cur_ch = s.charAt(j);
                if(set.contains(cur_ch)){
                    break;
                }
                set.add(cur_ch);
                len++;
            }
            maxLen = Math.max(len,maxLen);
            set.clear();
        }
        return maxLen;

    }
}

时间复杂度都到O(n^2)了,运行时间108ms,打败了15%哈哈哈。
那么下面来改进。

从暴力解法改进到快速解法

这里是借助start指针和set同时来完成。
我们用图来看嘛,这样更清晰一些。
在这里插入图片描述
假设当前我已经从i点开始找到j-1,这是一个不重复的字符子串,但是j号位重复了。
此时如果按照暴力解法的逻辑,我需要回到i+1,重置set,然后继续一个个查询。

但duck不必,我们直接从start开始往后遍历,将遍历到的字符从set中删去,知道遇到了在我们的子串中的X。
此时,我们并不回到i+1去,而是去到后一个X的下一位,并将start指针移动到前一个X的下一位因为从这个位置到第二个X,我们可以肯定这其中没有重复字符。此时,我们省去了很多开销,不用再重复比较。
在这里插入图片描述
思想主要是之前看kmp算法和manacher想到的,就是去重嘛。
该解法就快很多了5ms,击败87%,空间也有77%,还行吧。应该算是思路比较清晰的那种。

快速解法代码实现

开头注释是我从使用暴力解到最终快速解法的思路过程,可以直接忽略,这里不删是便于之后我自己复习。

public int lengthOfLongestSubstring(String s) {
        //首先暴力解法,每次用set与后面比较
        //当然了,暴力解法最慢了,所以要想一想如何改进
        //因为是关于字符串的,所以要想想能不能用manacher算法和kmp算法
        //肯定都不能用,不过有没有新的思路呢,能不能排除掉一些重复的?
        //有点难想啊,这个要是追求o(n)就真有点难
        //想到了,不用set,而用map来存储每个值的索引,这样的话,我就不用每次只移动一个
        //我只要移动到重复元素(前者)的后面一位就可以了。
        //同时,我要将所有的索引小于重复元素(前者)的全部删掉
        //思路详细的写一下,这里要有一个指针,来记录下当前的起点
        //后来起点的更新为重复元素的后一个索引
        //显然还能改进,因为这里用set会更好,你没有必要用map,因为你始终都是要删除它们的,
        //所以直接从start开始往后,找到cur_ch
        Set<Character> set = new HashSet<>();//键是值,value是索引
        int maxLen =0;
        int start = 0;
        int len;
        for(int i =0; i<s.length(); i++){
            len = i - start;
            for(int j = i; j<s.length(); j++){
                char cur_ch = s.charAt(j);
                if(set.contains(cur_ch)){//发现重复
                    //删除之前的点
                    int index = start;
                    while(s.charAt(index)!=cur_ch){
                        set.remove(s.charAt(index));
                        index++;
                    }
                    start = index+1;//重置起点
                    //更新i
                    i = j;
                    maxLen = Math.max(len,maxLen);
                    len = i-start+1;
                    break;
                }
                set.add(cur_ch);
                len++;
            }
            maxLen = Math.max(len,maxLen);
        }
        return maxLen;
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值