Loading

LeetCode 3.无重复字符的最长字串

 

时间:2020/03/24

 

一.题目描述

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

示例 1:

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

示例2:

输入: "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1

示例3:

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

 

 

二.算法

1.暴力法

题解:

通过双层循环找出s的所有子串,并对每个字串判定是否含有重复字符,在判定的过程中使用HashSet。使用该方法虽然可以,但是会超过题目要求的时间复杂度。

代码:

class Solution {
    public int lengthOfLongestSubstring(String s) {
        if("".equals(s)){
            return 0;
        }
        else{
            int len = s.length();
            int num = 1;
            for(int i = 0; i < len; i++){
                for(int j = i + 1; j < len; j++){
                    if(noRepetition(s, i, j))
                    num = Math.max(num, j - i + 1);
                }
            }
            return num;
        }
    }

    public boolean noRepetition(String s, int start, int end){
        Set<Character> set = new HashSet<>();
        for(int i = start; i <= end; i++){
            if(set.contains(s.charAt(i)))   return false;
            set.add(s.charAt(i));
        }
        return true;
    }
}

 时间复杂度:O(n3

 

2. 滑动窗口

概念:

滑动窗口是解决数组以及字符串问题的常用抽象概念。窗口通常是在数组/字符串中由开始和结束索引定义的一系列元素的集合,即[i, j)。而滑动窗口是可以将两个边界向某一方向“滑动”的窗口。例如,我们将[i, j)向右滑动1个元素,则它将变为[i+1, j+1)。注意:这里不一定左右边界都加1,可以只是一个边界加1。

题解:

开始时左右边界都为0,并且创建一个HashSet来存放整个窗口,使用HashSet的优点是查找的时间复杂度可以认为是O(1)。然后开始遍历,判断该位置的字符是否在set中,如果不在,需要添加到set中,如果在,则要判断当前窗口和num(即现有最长子串的长度)哪一个大,将大的赋值给num,之后还要让左边界右移一个单位。这里还有需要注意的一点:在循环外需要再判断一次。(这里与官方题解不同,算是一点小的优化)

代码:

class Solution {
    public int lengthOfLongestSubstring(String s) {
        int len = s.length();
        Set<Character> set = new HashSet<>();
        int num = 0, i = 0, j = 0;
        while(i < len && j < len){
            if(!set.contains(s.charAt(j))){
                set.add(s.charAt(j++));
            }
            else{
                num = Math.max(num, j - i);
                set.remove(s.charAt(i++));
            }
        }
        num = Math.max(num, j - i);
        return num;
    }
}

 时间复杂度:O(n)

 

 3.优化的滑动窗口

题解:

该方法在上面方法的基础上改用HashMap来存储字符,这样可以建立从子符到索引的映射,注意:在HashMap中,当键已经存在时,值会被覆盖。就是说,如果s[j]在[i, j)范围内有与当前字符相同的字符,假设该字符的位置是k,我们不需要逐渐增加i,可以直接跳过[i, k]范围内的所有字符,并将i变为k+1。

代码:

public class Solution {
    public int lengthOfLongestSubstring(String s) {
        int n = s.length(), ans = 0;
        Map<Character, Integer> map = new HashMap<>(); // current index of character
        // try to extend the range [i, j]
        for (int j = 0, i = 0; j < n; j++) {
            if (map.containsKey(s.charAt(j))) {
                i = Math.max(map.get(s.charAt(j)), i);  //look
            }
            ans = Math.max(ans, j - i + 1);
            map.put(s.charAt(j), j + 1);
        }
        return ans;
    }
}

注意:大家可能会对注释了look的那一行产生疑问,为什么要与i比较并取最大值呢?这里举一个例子:字符串s为abba。你自己在脑海中运行一遍这个例子就知道了。

时间复杂度:O(n)

 

4.我自己的方法

题解:

这是我做这道题时第一次想到的方法,主要思想就是遍历整个字符串,找到每个位置字符所能组成的最大的不重复的子串,然后取其中的最大长度就是答案。

代码:

class Solution {
    public int lengthOfLongestSubstring(String s) {
        int num = 0;
        if(s == null){
            return num;
        }
        else{
            char[] c = s.toCharArray();
            HashMap<Character, Integer> map = new HashMap<>();
            int nowNum = 0;
            int len = c.length;
            int index = 0;
            for(int i = 0; i < len; i++){
                if(!map.containsKey(c[i])){
                    nowNum++;
                    map.put(c[i], 1);
                }
                else{
                    if(nowNum > num){
                        num = nowNum;
                    }
                    nowNum = 0;
                    map.clear();
                    index++;
                    i = index - 1;
                    // System.out.println(map);
                }
            }
            if(nowNum > num){
                num = nowNum;
            }
        }
        return num;
    }
}

 

posted @ 2020-03-26 17:25    阅读(179)  评论(0编辑  收藏  举报