- 給定一個字串,請你找出其中不含有重複字元的 最長子串 的長度。
- 示例 1:
輸入: "abcabcbb"
輸出: 3
解釋: 因為無重複字元的最長子串是 "abc",所以其長度為 3。
可在 twoSum中切換解題方式
暴力解法
我們可以遍歷給定字串 s 的所有可能的子字串並判斷子字串是否有無重複。
如果沒有重複,那麼我們將會更新無重複字元子串的最大長度的答案。
-
時間複雜度:O(n^3)
要驗證索引範圍在 [i, j) 內的字元是否都是唯一的,我們需要檢查該範圍中的所有字元
對於所有 j∈[i+1,n] 所耗費的時間總和為:i+1∑n O(j−i)
對於給定的 i∈[0,n-1]
因此時間複雜度為O(n^3) -
空間複雜度O(n)
def check_str(self,s: str) -> bool:
for i in range(len(s)):
if s[i] in s[i+1:]:
return False
return True
def force(self, s: str) -> int:
m=0
if s != "":
m=1
for i in range(len(s)):
for j in range(i+1,len(s)):
if s[i] != s[j]:
if self.check_str(s[i:j+1]):
m = max(m,len(s[i:j+1]))
return m
視窗滑動法
在暴力法中,我們會反覆檢查一個子字串是否含有有重複的字元,但這是沒有必要的。
如果從索引 i 到 j - 1之間的子字串 s[i:j]已經被檢查為沒有重複字元。我們只需要檢查 s[j] 對應的字元是否已經存在於子字串s[i:j]中。
要檢查一個字元是否已經在子字串中,我們可以檢查整個子字串,這將產生一個複雜度為 O(n^2)的演算法,但我們可以做得更好。
透過使用 HashSet 作為滑動視窗,我們可以用 O(1)的時間來完成對字元是否在當前的子字串中的檢查。
滑動視窗是陣列/字串問題中常用的抽象概念。
- 視窗通常是在陣列/字串中由開始和結束索引定義的一系列元素的集合,即s[i,j).add()而滑動視窗是可以將兩個邊界向某一方向“滑動”的視窗。
例如,我們將 [i, j)向右滑動 1 個元素,則它將變為 [i+1, j+1)(左閉,右開)。 - 回到我們的問題,我們使用 HashSet 將字元儲存在當前視窗 [i, j)(最初 j = i)中。
- 然後我們向右側滑動索引 j,如果它不在 HashSet 中,我們會繼續滑動 j。直到 s[j] 已經存在於 HashSet 中。
- 此時,我們找到的沒有重複字元的最長子字串將會以索引 i 開頭。如果我們對所有的 i 這樣做,就可以得到答案。
- 時間複雜度:O(2n) = O(n)O,在最糟糕的情況下,每個字元將被 i 和 j 訪問兩次。
- 空間複雜度:O(min(m, n)),與之前的方法相同。滑動視窗法需要 O(k) 的空間,其中 k 表示 Set 的大小。而 Set 的大小取決於字串 n 的大小以及字符集 / 字母 m 的大小。
def sliding_window(self, s: str) -> int:
# 視窗的定義
dic = {}
# 定義視窗左邊和最大無重複子字串長度為0
i ,res = 0, 0
# 向右從0依次滑動視窗右邊j
for j in range(len(s)):
# 當視窗中的字串有重複
if s[j] in dic:
#將視窗左邊i向後移動
i = max(dic[s[j]], i)
# i +=1
# 獲取新的最大無重複子字串長度
res = max(res, j-i+1)
# 將j滑過的字元存入dic
dic[s[j]] = j+1
return res
python3最新執行速度最佳化題解,還是利用視窗移動方式
速度和佔用記憶體最優: 還是利用移動視窗 但是取消了字典或者set的定義,用python3 的切片來代替
def lengthOfLongestSubstring(self, s:str) -> int:
length,j = 0,-1
for i,x in enumerate(s):
if x in s[j+1:i]:
length = max(length,i-j-1)
j = s[j+1:i].index(x)+j+1
return max(length,len(s)-1-j)
原始碼儲存在github上,歡迎來提bug哦!-點選訪問
如果覺得不錯請給我一個star謝謝了Stray_Camel(^U^)ノ~YO
本作品採用《CC 協議》,轉載必須註明作者和本文連結
文章!!首發於我的部落格Stray_Camel(^U^)ノ~YO。