• 无重复字符的最长子串


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

    示例 1:

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

    示例2:

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

    示例3:

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

     解法一:暴力解决

    思想:逐个检查所有的子字符串,看它是否不含有重复的字符。

    算法:如果我们有一个函数allUnique(_ s: String, start: Int, end: Int) -> Bool如果子字符串的字符都是唯一的,它会返回true,否则会返回false。我们遍历给定字符串s的所有可能的子字符串并调用函数allUnique,如果事实证明返回值为true,那么我们将会更新无重复子串的最大长度的答案。

    为了枚举给定字符串的所有子字符串,我们需要枚举它们开始和结束的索引。假设开始和结束的索引分别为 i 和 j

    那么我们有0<=i<j<=n,我们使用i从0到n-1及j从i+1到n这两个嵌套的循环,枚举出所有子字符串。

    要检查一个字符串是否有重复字符,可以使用集合。我们遍历字符串中的所有字符,并将它们逐个放入set中,在放置一个字符之前,检查改集合是否已经包含它,如果包含,我们会返回false,循环结束后,我们返回true。

    let s: String = "abcabcbb"
    func lengthOfLongestSubstring(_ s: String) -> Int {
        var results: Int = 0
        for i in 0..<s.count {
            for j in i+1..<s.count + 1 {
                if allUnique(s, start: i, end: j) {
                   results =  max(results, j - i)//更新最长子字符长度
                }
            }
        }
        return results
    }
    
    func allUnique(_ s: String, start: Int, end: Int) -> Bool {
        var set: Set = Set<Character>()
        var characters = Array(s)//转为字符数组
        for i in start..<end {
            let ch: Character = characters[i]
            if set.contains(ch) {return false}
            set.insert(ch)
        }
        return true
    }
    
    let result = lengthOfLongestSubstring(s)
    print(result)

    解法二:滑动窗口法

    暴力法非常简单,但它太慢了。那么我们该如何优化它呢?

    在暴力法中,我们会反复检查一个子字符串是否含有有重复的字符,但这是没有必要的。

    要检查一个字符是否已经在子字符串中,我们可以检查整个子字符串,这将产生一个复杂度为 O(n2) 的算法,但我们可以做得更好。

    通过使用 HashSet 作为滑动窗口,我们可以用 O(1)O(1) 的时间来完成对字符是否在当前的子字符串中的检查。

    let s: String = "abcabcbb"
    func lengthOfLongestSubstring(_ s: String) -> Int {
        var set: Set = Set<Character>()
        var characters = Array(s)//转为字符数组
        var results: Int = 0
        var i: Int = 0
        var j: Int = 0
        while i < s.count && j < s.count {
            if !set.contains(characters[j]) {
                set.insert(characters[j])
                j += 1
                results = max(results, j - i)
            } else {
                set.remove(characters[i])
                i += 1
            }
        }
        return results
    }
    
    let result = lengthOfLongestSubstring(s)
    print(result)

    时间复杂度:O(2n)=O(n),在最糟糕的情况下,每个字符将被 i和 j 访问两次。

    空间复杂度:O(min(m,n)),与之前的方法相同。滑动窗口法需要 O(k) 的空间,其中 k 表示 Set 的大小。而 Set 的大小取决于字符串 nn 的大小以及字符集 / 字母 mm 的大小。

    以上就是两种方式,大家要着重理解一下后一种方式!!!

  • 相关阅读:
    WIFI芯片,结构光和ToF
    Ubuntu + Python
    React-Native 报错处理
    GPS && AGPS
    Feed流
    Kafka基础知识总结
    Kafka海量日志收集架构之Watcher监控告警-watcher 基础语法与使用
    4-23学习心得
    4-22 学习心得
    4-21学习心得
  • 原文地址:https://www.cnblogs.com/guohai-stronger/p/11750961.html
Copyright © 2020-2023  润新知