leetcode無重複字元的最長字串 python實現

王平發表於2019-04-16

​無重複字元的最長字串是一道字串處理演算法的題目,在日常程式設計中,處理字串是常見任務。用Python來實現leetcode這道演算法題,該題目會涉及到一個概念“滑動視窗”。

python實現無重複最長字串

一、題目描述

給定一個字串,請你找出其中不含有重複字元的 最長子串 的長度(Longest substring without repeating characters)。


示例 1:

輸入: "abcabcbb"
輸出: 3
解釋: 因為無重複字元的最長子串是 "abc",所以其長度為 3。

示例 2:


輸入: "bbbbb"
輸出: 1
解釋: 因為無重複字元的最長子串是 "b",所以其長度為 1。

示例 3:


輸入: "pwwkew"
輸出: 3
解釋: 因為無重複字元的最長子串是 "wke",所以其長度為 3。
請注意,你的答案必須是 子串 的長度,"pwke" 是一個子序列,不是子串。

 

二、解題思路

先來定義一下“子串”,根據題目描述,“子串”就是字串中擷取某一部分,長度從1到該字串的長度。比如“abc”的子串集合是:

[‘a’, ‘b’, ‘c’, ‘ab’, ‘bc’, ‘abc’]

用 Python 生成這個集合很容易,你是否想到了?


def subs(s: str):
    results = []
    for begin in range(len(s)):
        for length in range(len(s)-begin):
            results.append(s[begin:begin + length + 1])
    return results
z = subs('abcde')
print(z)

那麼問題來了,任意長度為n的字串一共有多少個這樣的“子串”呢?答案是:(n+1) * n / 2 。從上面的例子很容易得出這個答案:begin等於0時,長度可以有 n – 0 個,begin等於1時,長度可以有 n – 1個,以此類推,總數就是:

n + (n-1) + … + 1 => (n+1)*n / 2

 

(1)暴力解法

明白了“子串”的概念和獲取方法,自然而然的就得到了最樸素也是最“暴力”的解法:遍歷字串得到所有“子串”,然後判斷每個“子串”是否有重複字元,最終就會得到無重複最長子串了。

這個“暴力”演算法中,計算所有子串的時間複雜度是 O(n2),而判斷一個子字串是否有重複字元,又要從頭到尾遍歷一遍該字串,所有最終的時間複雜度可以達到 O(n3)。

這個解法是不能被接受的,提到它全是因為前面對“子串”的解釋及其數量計算,來練習Python對字串的操作。

 

(2)滑動視窗

“滑動視窗”這個概念在計算機演算法中非常常見。該演算法可以把巢狀的迴圈轉化為單迴圈從而降低時間複雜度。它在很多不同的領域都有應用:

TCP協議的滑動視窗進行流量控制

滑動視窗演算法介紹

NLP(自然語言處理)中的 N-gram

滑動視窗在nlp中的運用

影像處理中的物體識別

滑動視窗在影像處理中的運用

有興趣的同學可以深入瞭解上面提到的應用領域。

下面我們看看,“滑動視窗”如何進行字串處理。結合題目中的例子“abcabcbb”這個字串,我們來看看如何找它的無重複最長子串。

 

首先,我們定義視窗的兩端:begin和end,分別表示要找的子串的開頭和結尾。

 

開始的時候,begin和end都指向0的位置即‘a’,然後end不斷後移(視窗變寬),當遇到第二個‘a’時(遇見重複字元)就得到一個子串,其長度就是end和begin位置的差。

不重複最長字串演算法演示

如何判斷是否遇到了重複字元‘a’呢?需要一個字典作為輔助資料結構,把end從頭開始遇到的每個字元及其索引位置都放到字典裡面,end每次移動到新字元就查一下字典即可。

 

通過字典,我們遇到第二個‘a’時就可以找到存在字典裡面的第一個‘a’的位置。為了繼續尋找無重複子串,begin就要指向第一個‘a’後面一個的位置即‘b’。然後end繼續後移到‘b’,有發現它與前面的‘b’重複,計運算元串長度賦值給最大長度(需要比較),同時begin要移動第一個‘b’後面的位置即‘c’。

 

這樣依次移動end到字串末尾就可以找到最長的子串,“子串視窗”也就從頭移到了末尾。而只需要end從頭到尾的一次迴圈即可。

 

把這個過程用Python實現如下:


class Solution:
    def lengthofLongestSubstring(self, s: str) -> int:
        maxlen = 0
        memo = dict()
        begin, end = 0, 0
        n = len(s)
        while end < n:
            last = memo.get(s[end])
            memo[s[end]] = end
            if last is not None:
                maxlen = max(maxlen, end-begin)
                begin = max(begin, last + 1)
            end += 1
        maxlen = max(maxlen, end-begin)
        return maxlen

無重複最長字串python實現結果

提交後就可以看到結果。“執行時間”還只是個參考,再一次提交相同程式碼結果不是圖中的擊敗91%,而變成了百分之十幾。

 

leetcode相關演算法題

python leetcode 之兩數之和(two sum)

leetcode 兩數相加(add two numbers) Python程式設計實現

猿人學banner宣傳圖

我的公眾號:猿人學 Python 上會分享更多心得體會,敬請關注。

***版權申明:若沒有特殊說明,文章皆是猿人學 yuanrenxue.com 原創,沒有猿人學授權,請勿以任何形式轉載。***

相關文章