无重复字符的最长子串

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

示例 1:算法

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

示例2:数组

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

示例3:函数

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

 

 解法一:暴力解决优化

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

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

为了枚举给定字符串的全部子字符串,咱们须要枚举它们开始和结束的索引。假设开始和结束的索引分别为 i 和 jhtm

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

要检查一个字符串是否有重复字符,可使用集合。咱们遍历字符串中的全部字符,并将它们逐个放入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 的大小。

 

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

相关文章
相关标签/搜索