Leetcode 3. Longest Substring Without Repeating Characters 無重複字元的最長子串
Leetcode 3. Longest Substring Without Repeating Characters 無重複字元的最長子串
標籤: Leetcode
題目地址:https://leetcode-cn.com/problems/longest-substring-without-repeating-characters/
題目描述
給定一個字串,請你找出其中不含有重複字元的最長子串的長度。
示例 1:
輸入: “abcabcbb”
輸出: 3
解釋: 因為無重複字元的最長子串是 “abc”,所以其長度為 3。
示例 2:
輸入: “bbbbb”
輸出: 1
解釋: 因為無重複字元的最長子串是 “b”,所以其長度為 1。
示例 3:
輸入: “pwwkew”
輸出: 3
解釋: 因為無重複字元的最長子串是 “wke”,所以其長度為 3。
請注意,你的答案必須是子串的長度,"pwke"是一個子序列,不是子串。
演算法思想
雜湊表法
做了這幾次題,發現雜湊表還真是個好東西,因為雜湊表的特殊儲存結構,所以使得其檢索起來比較快,所以在處理字串陣列的時候選擇雜湊表是不錯的選擇。
思想是:
1、遍歷字串如"pwwkew"
2、用雜湊表(python中可用字典)把字元和字元在的位置存起來如:{"p":0}
3、在存起來之前要先看這個字元是否出現過。
------3.1、如果沒有,就存起來。
------3.2、否則說明之前出現過,說明當前子串開始重複,所以記錄當前的長度與當前最大長度比較,並且把雜湊表置為空。並且把i回退到重複的位置。(比如pwabwuipq,如果i=4,那麼說明開始重複了,和1位置的w重複了,所以要回退到1位置的後面,因為1後面的並沒有重複)
舉個栗子
初始化:
str = “pwawkew”
dict ={}
maxLength,tempLength = 0
i | dict | tempLength | maxLength |
---|---|---|---|
0 | {“p”:0} | 1 | 0 |
1 | {“p”:0,“w”:1} | 2 | 0 |
2 | {“p”:0,“w”:1,“a”:2} | 3 | 0 |
3->2(回退回去) | {“p”:0,“w”:1,“a”:2} ->{} | 0 | 3(因為w在dict中有,所以開始重複,此時把tem與max比較,把max置為大的那個) |
2 | {“a”:2} | 0 | 3 |
… |
因為有回退的操作,所以這個演算法的效率並不是很高,提交後雖然AC但是時間為700ms。
python程式碼
# 784ms
class Solution(object):
def lengthOfLongestSubstring(self, s):
"""
:type s: str
:rtype: int
"""
dict = {}
maxLength = 0
temLength = 0
i = 0
while i<len(s):
if s[i] in dict:
# 因為最後一行有i+=1所以這個地方不是回退到dict[s[i]]+1位置
i = dict[s[i]]
if temLength>maxLength:
maxLength = temLength
dict = {}
temLength = 0
else:
dict[s[i]] = i
temLength+=1
i+=1
return (maxLength if(maxLength>temLength) else temLength)
演算法改進
通過上面的執行可以看出非常耗時,並且也分析出來了原因,主要是因為在發現重複的時候進行了一步回退的操作,所以造成時間的浪費,那麼我們可以考慮一下不進行回退,由於我們已經儲存了之前重複的位置,所以重複元素之間的那段距離可以直接通過減法計算得到(temLength = i - dict[s[i]])
。
但是這樣又會造成一個問題,由於那段距離我們是通過計算得到的,所以我們不能進行一個向dict裡面放元素的操作,這樣的話意味著我們dict的元素不能置為{},還拿上面的那個例子str = "pwawkew"
來說,dict = {{"p":0,"w":1,"a":2}}
,這個時候w重複了,我們本來要把dict置為{},但是現在我們直接計算了waw這段距離,所以我們不能把dict置為{},因為不能保證後面是否還會有a,這樣的話我們p也會存在裡面,但是如果後面有p的話,其實並不算重複,因為這個p在1的位置的w之前,所以相當於已經刪除了(因為我們計算的waw這段距離裡面沒有算p),所以這個時候我們的判斷條件就要變了。
如果我們遇到了一個dict裡面有的元素,我們要看它是屬於以前的用過的,還是剛才新增進來的,這裡我們使用一個距離就能表示,因為如果這個重複的是之前的那麼它的位置+tempLength肯定小於當前位置。
所以這個時候如果在字典中有檢查到出現,那麼需要再多一步判斷,也就是3.2的操作進行多一步判斷,這個重複的是在當前串中的,還是以前刪掉的串中的。程式碼中就是if dict[s[i]] +temLength <i:
這個判斷。
python程式碼
# 60ms
# ref https://blog.csdn.net/littlebai07/article/details/79100081
class Solution(object):
def lengthOfLongestSubstring(self, s):
"""
:type s: str
:rtype: int
"""
dict = {}
maxLength = 0
temLength = 0
i = 0
while i <len(s):
# 不重複的了
if (s[i] not in dict):
dict[s[i]] = i
temLength +=1
else:
# 是和前面以前刪除的重複了
if dict[s[i]] +temLength <i:
temLength +=1
# 真正重複了
else:
maxLength =maxLength if(maxLength>temLength) else temLength
temLength = i - dict[s[i]]
dict[s[i]] = i
i+=1
return (maxLength if(maxLength>temLength) else temLength)
參考: