位元組跳動2018.9.9筆試 最長不重複子串
題目描述
給定一個字串,請找出其中無重複字元的最長子字串的長度。例如,“abcabcbb”,其無重複字元的最長子字串實“abc”,其長度為3。“bbbbb”,其無重複字元的最長子字串是“b”,長度為1。 此題是leetcode第三題原題。
本人思路
以上圖為例,已經掃描到了abc了: 如果迴圈到了字元a,與當前掃描到的字串中的a重複了,那麼新的字串就應該從a之後開始到當前迴圈字元。 如果迴圈到了字元b,與當前掃描到的字串中的b重複了,那麼新的字串就應該從b之後開始到當前迴圈字元。 … 如果迴圈到了字元d,與當前掃描到的字串中每個字元都不重複,那麼新的字串就應該從起始位置到當前迴圈字元。
程式碼
s = input()
n = len(s)
temp = []#儲存當前掃描到的無重複最長子串
maxleng = 0
for i in range(n):
if(s[i] not in temp):#如果不在temp裡就新增進去
temp.append(s[i])
else:#如果在裡面,檢視temp的長度是否比maxleng大
if len(temp)>maxleng:
maxleng = len(temp)
index = temp.index(s[i])#獲得當前重複的索引
temp = temp[index+1:len(temp)]#新的temp是從重複字元之後開始
temp.append(s[i])#再添加當前重複字元
if len(temp)>maxleng:#計算最後一次
maxleng = len(temp)
print(maxleng)
但此程式碼有個弊端,就是需要temp陣列來存當前掃描到的無重複子串,實際是不需要的。
最優思路
上LeetCode提交我的程式碼,執行時間124ms,打敗了44%的人,可見我這個演算法之差啊。但由於我提交的程式碼是python3,在LeetCode裡面也只能看見python3裡面的最優程式碼(80ms),下面我將對最優程式碼進行講解。
class Solution:
def lengthOfLongestSubstring(self, s):
"""
:type s: str
:rtype: int
"""
start, longest, store = 0, 0, {}
for i, c in enumerate(s):
if c not in store:
store[c] = i
else:#當前c已經出現過了,但不一定在當前掃描到的無重複子串中
#記錄下當前c之前位置掃描到無重複子串的長度
longest = max(longest, i - start)
#更新無重複子串的開始位置,為下一次記錄作好準備
#如果當前c不在當前掃描到的無重複子串中,start不會更新
start = max(start, store[c] + 1)
#更新每個字元的最新位置
store[c] = i
longest = max(longest, len(s) - start)#記錄最後一次
return longest
此程式碼相比我的程式碼,優點在於不需要temp陣列儲存當前掃描到無重複子串,而是用start記錄無重複子串的開始索引。
但是程式碼的思想和我的是很類似的。
和最優程式碼的進入else的條件不一樣,我是在temp裡有重複的就進入,所以無重複子串的起始位置肯定會更新;最優程式碼是隻有字元是第二次出現就進入else,但不一定會更新無重複子串的起始位置。
start
索引記錄無重複子串的起碼位置,i
記錄的是i-1
是無重複子串的終止位置。我的程式碼中,是根據當前字元是否與temp有重複,如果有就進入else,進入else後起始位置start必更新。最優程式碼中,是根據當前字元是否出現過,如果出現過就進入else,進入else後起始位置start不一定更新,因為有可能當前字元上一次出現索引在start前面,這樣的話start就不會更新。
start更新後,就為下一次進入else作好了準備,下一次執行longest = max(longest, i - start)
,i - start
則就是下一次掃描到的無重複子串的長度。
再分析終止位置吧,當迴圈變數到了i
時,終止位置則為I-1
,看我畫的第一個圖就能明白。