1. 程式人生 > >LeetCode系列:3 Longest Substring Without Repeating Characters

LeetCode系列:3 Longest Substring Without Repeating Characters

Q:Given a string, find the length of the longest substring without repeating characters.

Example 1:

Input: "abcabcbb"
Output: 3 
Explanation: The answer is "abc", with the length of 3. 
Example 2:

Input: "bbbbb"
Output: 1
Explanation: The answer is "b", with the length of 1.
Example 3:

Input:
"pwwkew" Output: 3 Explanation: The answer is "wke", with the length of 3. Note that the answer must be a substring, "pwke" is a subsequence and not a substring.

E:給定一個字串,計算出不重複的最長子串的長度。

e.g.
Example 1:
輸入: "abcabcbb"
輸出: 3
解釋: 最長不重複子串是"acb"且長度為3

Example 2:
輸入: "bbbbb"
輸出: 1
解釋: 最長不重複子串是"b"且長度為1 Example 3: 輸入: "pwwkew" 輸出: 3 解釋: 最長不重複子串是"wke"且長度為3,請注意答案必須是子串,"pwke"是子序列而不是子串

A:

因為只需要計算最長長度,不需要記錄子串,所以我們可以利用Dictionary或者Set不能重複的特性來儲存當前遍歷過的串,遇到相同字元,也就是key存在重複。

Approach 1:

方法一先考慮使用Dictionary的實現方式。這裡key是遍歷的字元,value是該字元在該字串中的序號(不是下標)。我們使用一個變數記錄當前子串的開始下標,當遇到重複字元的時候,這時我們需要去比較當前子串和上一個子串(若沒有則為0)的長度,更新當前子串的開始下標(重複字元的下標加一或者還是當前開始下標),更新重複字元的下標,直到字串遍歷結束。

class Solution {
    func lengthOfLongestSubstring(_ s: String) -> Int {
        var begin = 0		//當前子串的開始下標
    	var maxLength = 0	//當前獲取的子串的最長長度
    	var map : [Character : Int] = [:]   //key為字元,value為字元的序號(下標+1)
    	for (index, value) in s.enumerated() {//普通for迴圈與enumerated()效能差別很大,所以儘量使用enumerated(),在這個演算法中,使用不同的for迴圈,效能相差達到100倍
        
        	if let existingIndex = map[value] {	//存在重複字元
            	begin = max(existingIndex, begin)	//更新當前子串的開始下標
        	}
        	maxLength = max(maxLength, index-begin+1)//更新當前獲取到的子串的最大長度
        	map[value] = index+1	//更新重複字元的下標
        	// print(map)
    	}
    	return maxLength
    }
}
Complexity Analysis:
  • 時間複雜度: O(n)。(LeetCode:64 ms)
  • 空間複雜度:O(n)。

Approach 2:

方法二先考慮使用Set的實現方式。思路和方法一差不多,沒有遇到相同字元的時候,將字元加入到Set中;遇到相同的字元時,更新最長子字串的長度,更新當前子串的開始下標,同時判定當前字串是不是重複的字元,若不是,將字元從Set中移除,當前子字串的開始下標+1直到遇到相同字元。(如字串是:“abcdecf"子字元是"abcde”,這時遇到相同字元’c’,需要將"ab"從Set中移除,begin指到第一個’c’的位置)。與方法一不同的是,如果最長的子字元是最後那個子串,這樣是遍歷不出的,所以還需要做一次比較:max(maxLength, charArray.count-begin)。

class Solution {
    func lengthOfLongestSubstring(_ s: String) -> Int {
        var maxLength = 0
        var begin = 0
        var set = Set<Character>()
        let charArray = Array(s)
        for (index, char) in charArray.enumerated() {
            if set.contains(char) {
                maxLength = max(maxLength, index-begin)
                while charArray[begin] != char {
                    set.remove(charArray[begin])
                    begin += 1
                    //print(set)
                }
                begin += 1
                //print(set)
            } else {
                set.insert(char)
                //print(set)
            }
        }
        return max(maxLength, charArray.count-begin)
    }
}
Complexity Analysis:
  • 時間複雜度: O(n)。(LeetCode:76 ms)
  • 空間複雜度:O(n)。

Approach 3:
class Solution {
    func lengthOfLongestSubstring(_ s: String) -> Int {
         var hash = [Int](repeating: 0, count: 256)
        let str = Array(s.utf8).map { Int($0) }
        
        let len = str.count
        var ans = 0
        var j = 0
        for i in 0..<len {
            while j < len && hash[str[j]] == 0 {
                hash[str[j]] = 1
                j += 1
                print(hash)
            }
            ans = max(ans, j - i)
            hash[str[i]] = 0
            print(hash)
        }
        return ans
    }
}
Complexity Analysis:
  • 時間複雜度: O(n)。(LeetCode:36 ms)
  • 空間複雜度:O(n)。