LeetCode-3 无重复字符的最长子串

题目:

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

示例:

给定 "abcabcbb" ,没有重复字符的最长子串是 "abc" ,那么长度就是3。

给定 "bbbbb" ,最长的子串就是 "b" ,长度是1。

给定 "pwwkew" ,最长子串是 "wke" ,长度是3。请注意答案必须是一个子串"pwke" 是 子序列 而不是子串。


方法一:

       不重复可以用List的contains(object obj)实现,后面就很简单了,定义max为最长字串长度,初值为0。从起点开始不断扫描,contains(obj)返回false就将其加入List,返回true就计算List长度,与max比较。接下来将起点+1同时clear()掉List,继续这个过程即可,代码就不贴了,比较简单。出乎意料,这样的暴力法也AC了,但是用时很长(超过5%的AC代码)

方法二:

       接下来就是想如何去优化它。

  1. 不重复可以用哈希表实现,因为ASCII码只有256个(题目中用到更少),只要定义一个大小为256的boolean数组hash,有字符c则将hash[c]置为true。
  2. List也是而安全可以不用的,以为hash数组已经记录了List中的内容,我们只需另外定义一个整数window(可以理解为窗口)作为方法一中List的长度,当出现重复时将max与window大者赋给max即可。
  3. 在方法一中将List清空相当于将之前的工作全部作废了,其实可以保留部分。例如,对于串 "bacdaea" ,进行到第二个 a 时不必将hash数组全部置为false,只要将第一个a前面的元素对应位置置为false(即hash[(int)b]=false),同时调整当前窗口大小即可。

 我用串"bacdaea" 进行举例,初始时 window=0,,max=0,hash数组全为false。

遍历字符串,从i=0到i=3,window和max都逐步增加到4,hash数组下标为“b","a","c","d"的 boolean 值都为true,不重复的字串为"bacd"。

i = 4,此时扫描到"a",hash[a]=true,则从i-window=0开始寻找第一个"a",找到位置记作start=1,遍历 [i-window,start) 区间的字符charc,令hash[charc]=false,最后将window调整为i-start=3。此时不重复的字串为”cda"。

i = 5,"e"不重复,置hash[(int)e]=true,window变为4,不大于max,max不变,此时字串为"cdae".

i = 6,类似i=4时,start=4,window变为2,到这里原字符串就遍历完了,最后返回max=4即可。代码如下:

class Solution {
    public int lengthOfLongestSubstring(String s) {
        if(s == null ||s.length() < 1)
            return 0;
        boolean[] hash= new boolean[256];
        int max = 0;
        int window = 0;//窗口大小
        for(int i=0;i<s.length();i++){
            if(hash[s.charAt(i)]){//重复了
                //从i-window开始找,找到重复字符
                int start = s.indexOf(s.charAt(i), i-window);
                //窗口推进到重复字符后
                for(int j=i-window;j<start;j++){
                    hash[s.charAt(j)] = false;
                }
                //缩小窗口(大小一定是缩小或者不变,所以不必和max比较)
                window = i-start;
            }
            else{
                hashMap[s.charAt(i)] = true;
                if(++window > max)
                    max = window;
            }
        }
        return max;
    }
}

      我的代码运行时间是36ms,超过91%AC代码。然而最快的代码只需要26ms,我看完后发现与我的思路相似,但是去掉了不必要的步骤。

方法三:

       仔细观察字符串"abcdacebde" ,当我们扫描到第二个字符"a"时,我们发现了重复的"a",自然的窗口移动到“a"后,变为"bcda",再移到"c"时,同样窗口移动为"dac",可以发现我们只要比较窗口中的所有字符与当前字符(第i个)是否相等就好了,无重复则窗口+1,有重复则窗口移动,即:窗口右端恒定+1,左端不移动或者移动到重复字符之后。

      这样分析就知道了hash数组是没有必要的,我们只要定义窗口的左右left和right就可以到我们的目的。我们只要从窗口左端left向右挨个字符和第i个字符比较,如果都不等,则left不变;如果找到一个charAt(j) = charAt(i),令left = j+1即可。代码如下:

class Solution {
    public int lengthOfLongestSubstring(String s) {
        if(s == null || s.length() == 0)
            return 0;
        int longest = 1;
        int left = 0, right = 1;
        while(right <= s.length() - 1) {
            char digit = s.charAt(right);
            for(int i = left; i < right; i++) {
                if(digit == s.charAt(i)) {
                    left = i + 1;               
                    break;
                }
            }
            longest = Math.max(longest, right + 1 - left);
            right++;
        }
        return longest;
    }
}

友情链接,软安同学的博客 -->ma5ker

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值