• 3_Longest Substring Without Repeating Characters


    3_Longest Substring Without Repeating Characters

    一、题目详情

      题意很清晰,给定字符串,求出不含重复字母的最长子串的长度。

    二、解题方法

    第一种方法:直接暴力搜索

      这种方法,求出给定字符串的每一个子串,判断子串是否满足没有重复字母的条件,由此求出最长的复合条件的子串。其中求字符串的所有子串,设定两个变量ij,i是子串的开始位置,j是子串的结束位置。通过两层循环i=0:n以及j=i+1:n,使用substring(i,j)方法求出所有子串。详细代码如下:
      class Solution {
        public int lengthOfLongestSubstring(String s) {
            int res = 0;
            for(int i=0;i<s.length();i++)
                for(int j=i+1;j<=s.length();j++)
                    if(allUnique(s.substring(i,j)))
                        res = Math.max(res,j-i);
            return res;
        }
      
      public boolean allUnique(String line){
          Set<Character> set = new HashSet<>();
          char[] chs = line.toCharArray();
          for(int i=0;i<chs.length;i++)
              if(set.contains(chs[i])) return false;
              else set.add(chs[i]);
          return true;
      }
    }    
    

      可以直观的看出allUnique()方法中还有一层循环,所以这种算法的时间复杂度为O(n^3).

    第二种方法:滑动窗口

      第一种方法十分naive,在leetcode上是不可能通过的,绝对TLE。但是我们可以重新来看看上一种方法,它比较了每一个字符串。假设i,j循环进行到i=i',j=j'的时候,我们得到substring(i',j')子串不满足条件时,i=i',j>j'的所有子串都不满足条件可以直接跳过。 所以我们将代码进行改进:
    class Solution {
        public int lengthOfLongestSubstring(String s) {
            int res=0,i=0,j=0;
            char[] chs = s.toCharArray();
            int n = chs.length;
            if(n==0) return 0;
            Set<Character> set = new HashSet<>();
            while(i<n&&j<n){
                if(!set.contains(chs[j])){
                    set.add(chs[j]);
                    res = Math.max(res,j-i+1);
                    j++;
                }else{
                    set.remove(chs[i]);
                    i++;
                }
            }
            return res;
        }
    }
    

      为了方便我们使用了hashSet来对子串的字母进行存储。如果set包含chs[j],i就直接+1,并且(i+1)--j中符合条件的子串肯定没有之前已经记录下来的最大长度还长,所以j可以接着继续++,不需要将j置为i+1.
      最差的情况下字符串的每一个字母都会被i和j分别遍历一次,所以时间复杂度为O(2n)=O(n).

    第三种方法:进一步改进

      前一种算法为O(2n),可以通过测试了,但是我们还可以进一步思考一下。如果当我们遍历到chs[j]时(chs是给定字符串的字符数组),如果不满足条件,并且chs[j] = chs[j'](j'输入[i,j)),这是我们可以直接将i设置为j'+1,因为i在j'之前和j构成的子串必定会因为chs[j']==chs[j]而不满足条件,我们用map来存储字母和下标的对应关系。详细代码如下:
    class Solution {
        public int lengthOfLongestSubstring(String s) {
            int res=0,i=0,j=0;
            char[] chs = s.toCharArray();
            int n = chs.length;
            if(n==0) return 0;
            Map<Character,Integer> map = new HashMap<>();
            while(i<n&&j<n){
                if(!map.containsKey(chs[j])){
                    map.put(chs[j],j);
                    j++;
                    res = Math.max(res,j-i);
                }else{
                    int ind = map.get(chs[j]);
                    map.put(chs[j],j);
                    i = Math.max(i,ind+1);//*
                    j++;
                    res = Math.max(res,j-i);
                }
            }
            return res;
        }
    }
    

      注意*行,要确保i至少得和正常遍历时的i一样大(即i = Math.max(i,ind+1)),不能比起小,因为正常遍历的i之前的字母已经被前面的步骤计算过了。

  • 相关阅读:
    AngularJs 与Jquery的对比分析,超详细!
    身份证号验证,获取户口地址、性别、出生日期
    前端面试·
    页面可见性(Page Visibility API) 可以有哪些用途?
    webSocket如何兼容低浏览器?(阿里)
    如何实现浏览器内多个标签页之间的通信?
    HTML5的form如何关闭自动完成功能?
    Label的作用是什么?是怎么用的?
    cookies,sessionStorage 和 localStorage 的区别?
    浏览器是怎么对HTML5的离线储存资源进行管理和加载的呢?
  • 原文地址:https://www.cnblogs.com/Mrfanl/p/11674510.html
Copyright © 2020-2023  润新知