• 3#无重复字符的最长子串


    题目描述

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

    示例 1:

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

    示例 2:

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

    示例 3:

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

    其实这一题的解释在官方题解里面已经写得比较清楚了,现在只是用自己的话二次解释一遍。

    思路

    方法一:暴力破解

    先编写一个函数boolean allUnique(String substring),如果substring中的字符都是唯一的,则返回true;否则返回false。一个比较方便的实现方法是利用HashSet,遍历这个子字符串中的每一个字符,HashSet中没有当前字符就加入,有的话就返回false

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

    再利用这个函数,根据substring的长度不同,遍历给定字符串s,设s的长度为length

    • substring长度为 1 时,遍历次数为 length
    • substring长度为 2 时,遍历次数为 length - 1
    • substring长度为 3 时,遍历次数为 length - 2
    • ……
    • substring长度为 length 时,遍历次数为 1

    这样算下来,暴力破解的过程中,要调用函数boolean allUnique(String s, int start, int end)的次数为n(n-1)/2。再考虑到boolean allUnique(String s, int start, int end)每一次调用,都会访问给定字符串n次。所以时间复杂度为O(N^3)

    源代码

    public class Solution {
        public int lengthOfLongestSubstring(String s) {
            int n = s.length();
            int ans = 0;
            for (int i = 0; i < n; i++)
                for (int j = i + 1; j <= n; j++)
                    if (allUnique(s, i, j)) ans = Math.max(ans, j - i);
            return ans;
        }
    
        public boolean allUnique(String s, int start, int end) {
            Set<Character> set = new HashSet<>();
            for (int i = start; i < end; i++) {
                Character ch = s.charAt(i);
                if (set.contains(ch)) return false;
                set.add(ch);
            }
            return true;
        }
    }
    

    方法二:滑动窗法

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

    使用一个HashSet来实现滑动窗,用来检查重复字符。向右侧滑动索引j,遇到不重复的字符就把它放进窗里,更新长度;遇到重复的字符,向右侧滑动索引i。重复以上操作直到遍历整个字符串。

    源代码

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

    在最坏情况下,每个字符被ij访问了两次。

    其实我觉得滑动窗有点类似于双指针的做法,都是维护两个指针,两个指针的移动速率不同,对一个线性集合操作,最后达到我们想要的结果。

    方法三:优化滑动窗

    除了使用HashSet判断重复字符,我们还可以建立字符到索引的映射,即可以使用HashMap判断重复字符。碰到重复字符,不需要一个个移动i,直接将i变为j+1

    源代码

    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);
            }
            ans = Math.max(ans, j - i + 1);
            map.put(s.charAt(j), j + 1);
        }
        return ans;
    }
    

    因为我们知道给定字符串的字符种类比较少,只有字母和数字,字符集比较小(都是 ASCII 码),所以可以进一步优化,使用整数数组替代HashMap,用来建立字符和索引的映射。

    public int lengthOfLongestSubstring(String s) {
        int n = s.length(), ans = 0;
        int[] index = new int[128]; // current index of character
        // try to extend the range [i, j]
        for (int j = 0, i = 0; j < n; j++) {
            i = Math.max(index[s.charAt(j)], i);
            ans = Math.max(ans, j - i + 1);
            index[s.charAt(j)] = j + 1;
        }
        return ans;
    }
    
  • 相关阅读:
    centos7安装Python3.7,执行./configure时报错,configure: error: no acceptable C compiler found in $PATH
    Hadoop集群搭建
    jdk安装
    ssh免密登陆
    centos安装python3.7
    centos7更改yum源
    32.Java基础_异常
    31.Java基础_日期/日期格式/日历类
    1.华为路由交换技术_网络基础知识
    396. 旋转函数(数学)
  • 原文地址:https://www.cnblogs.com/yuzhenzero/p/10215150.html
Copyright © 2020-2023  润新知