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之前的字母已经被前面的步骤计算过了。

posted @ 2019-10-14 22:02  Mrfanl  阅读(143)  评论(0编辑  收藏  举报