[每日一題]3. Longest Substring Without Repeating Characters

2021-02-21 每日一道算法題

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

Examples:

Given "abcabcbb", the answer is "abc", which the length is 3.

Given "bbbbb", the answer is "b", with the length of 1.

Given "pwwkew", 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.

王小二一大早拿了一個題目去找老馬幫忙。因為他的大學同學在同學群裡發了一道 Google 面試題,他想找老馬聊聊思路。

老馬看了看王小二手中的題目,大概的意思是輸入一個純小寫英文字母組成的字符串,輸出該字符串的最長的沒有重複字母的子串的長度。老馬一直很滿意王小二的勤奮好學,所以還是非常樂意跟王小二分享算法方面的思路。

老馬先問王小二自己的想法:「小二, 這道題你自己有什麼思路嗎?」

王小二回答道:「思路倒是有,我想到的方案是暴力法。」

「怎麼一個暴力法?具體說說吧!」

「就是用暴力方法,遍歷字符串所有的位置,以這個位置往前走,直到出現重複字母就停下來。然後返回所有結果中符合要求的字符串的長度的最大值。」 王小二擔心老王沒有挺清楚,接著解釋道:「具體地,可以用兩個指針維護子串的區間,每往前走一格,需要判斷當前位置的字符在區間是否重複。」

老馬點點頭:「你的這個暴力方法的時間複雜度是多少?」

王小二不好意思的說:「O(N^3)」

「嗯,複雜度有點高啊」 老馬笑著說:「你有想到優化的辦法嗎?」

王小二用手託著下巴想了一會後,不好意思的說:「沒有啥好辦法。」

「其實我們可以用 Hash 或者 Set 來降低查詢的時間複雜度」 老馬說:「這是一種非常常見的空間換時間的手法。你應該知道,在數組中搜索一個元素的複雜度是 O(N), 但是在Hash或者Set 中搜索一個元素的複雜是 O(1)。」

老馬指了指王小二帶來的題目接著道:「結合你剛才的解法,其實我們可以把每次搜索過的字符加入 Hash/Set , 這樣判斷元素是否重複部分的時間複雜度可以降到 O(1), 這樣整體的時間複雜度可以降低到 O(N^2)。」

王小二真是佩服老馬,三言兩語就能把時間複雜度降低一個數量級。

老馬接著說 :「但是時間複雜度還是太高了。」

王小二點頭說:「是的。如果能將複雜度降低到O(N) 就好了。」

老馬說:「將這種類型的題目的複雜度降低到 O(N) 的方法還是很多的。我今天跟你講講兩個思路吧。」

「第一個思路叫做 two pointers, 中文名叫雙指針。雙指針在鍊表問題中應用特別廣泛,特別是在鍊表環路判斷和排序等方面。」

「本題的雙指針的用法是這樣的:分別用i, j 表示符合條件的字符串的左邊和右邊。如果 s[j+1] 不在當前的子串中, 那麼將s[j+1] 加入子串中,移動 j ; 如果 s[j+1] 存在於當前的子串中,那麼移動 i 到 當前子串 s[j+1] 字符出現的位置。 」

「懂了」 王小二讚嘆到  「這種解法真是精妙啊,只需要掃描一次字符串,時間複雜度降低到了 O(N) 。」 

「嗯,還有一個方法,叫做滑動窗口」 老馬見王小二理解了雙指針的思路,就接著講起了滑動窗口。

「滑動窗口的思路在本題上跟雙指針法方法有點像,就是維護一個 [i, j ] 的區間,其他的操作跟 雙指針一樣。」

「當然」老馬頓了頓說 :「其實也可以不一樣。我們可以用一個 Queue 來表示滑動窗口,當 s[j+1] 在 Queue 中的時候,我們就彈出這些元素:隊列的頭部到隊列中的s[i]的位置的元素。」

「這樣做有哪些不好的地方?」老馬突然拋出一個問題。

「我覺得用隊列來表示滑動窗口有兩個缺點:1,空間浪費,需要維護一個最大 N 大小的空間;2,時間複雜度也比雙指針要差,因為隊列每次只能移動一步,但是雙指針可以跳躍移動,更靈活。」

「對」,老馬欣慰的點點頭,王小二學習能力還是很強的。

「還有其他解法嗎?」 老馬接著問道。

「已經有三種解法啦,還有其他的解法?」 他表示很吃驚。

「一定有」, 老馬肯定的答道:「這道題還有一種非常精妙的解法,應該跟你分享一下。」

「啥方法」 王小二有點迫不及待了。

老馬沒有直接回答,而是說:「我們不是剛剛說了暴力嘛,我們的方案是不是從左往右掃描的?」

「是的」王小二肯定的答道。

「那你覺得從右往左掃描怎麼樣?」老馬笑著說。

王小二想了想,拍了拍自己的腦袋「對啊,如果從右往左掃,會降低複雜度。」

「這樣不會降低複雜度,是一種逆向思維」老馬示意他繼續講下去。

王小二接著說「假設當前的位置是 i , 那麼以 s[i] 結尾的符合要求的子串的最大長度有兩種可能。」

「如果s[i] 與前面的子串中的字符沒有重複,那麼最大長度就是 i + 1」

「如果s[i] 與前面的子串中的字符有重複, 那麼最大長度就是 i - 離i最近的出現字符s[i]的位置 + 1"

「對」 老馬高興的說 :「你分析的很對。你的這個暴力方案還可以在優化嗎?」

「可以」, 王小二說:「你剛才分享了用Hash 的方式可以降低在一個 容器中尋找元素是否存在的時間複雜度。我覺得這個方案也可以用這個辦法。」

「贊啊!的確可以這麼做」 老馬說:「其實這種方法叫做記憶優化,就是我們緩存部分結果來減小時間複雜度。」

「小二,要不你把我們今天的談話總結一下吧?」老馬提了一個小要求。

「好的」,王小二愉快的答應了,畢竟他覺得今天的收穫還是很大的:「我明天早上給你。」

「對了這個題目還可以用 二分法和分治算法,有空你可以練練代碼。」 老馬給王小二布置了一個家庭作業。

晚上睡前前,王小二開始做總結,他想了想跟老馬的討論,寫下了如下幾個重點:

靈活運用數據結構,比如用 Hash/Set 等容器能將搜索元素的複雜度降低到 O(1)

雙指針 和 滑動窗口 在解決連續串的問題非常有效(連續的字符串或者鍊表),雙指針更靈活

對比思考:從右往左 比 從左往右 往往可以得到一些新的思路;將 i 作為條件終點,比將 i 作為條件起點能讓問題更簡單

掃描的過程中,做記憶優化可以顯著的降低時間複雜度

相關焦點

  • [leetcode] 3. Longest Substring Without Repeating Characters
    Giv
  • 3. Longest Substring Without Repeating Characters
    題目連結:https://leetcode-cn.com/problems/longest-substring-without-repeating-characters
  • Longest Substring Without Repeating Characters
    from a thought, a problem or a character.Increasing and not cut, never backing away firmly is the key to solve the slide window problem.In this problem, what has returned is the length and size of the longest
  • LeetCode Top 100 高頻算法題 03:Longest Substring
    小編和實驗室同學之前面試找工作,也只刷了劍指offer和這top 100算法題,在實際面試中也遇到了很多LeetCode上的原題。劍指offer算法最優解之前和大家分享了,LeetCode Top 100這100道算法題,每道題小編都刷了很多遍,並且總結了一種最適合面試時手撕算法的最優解法。後續每天和大家分享一道LeetCode top 100高頻算法題,以及小編總結的最優解法。
  • LeetCode Top 100 高頻算法題 05:Longest Palindromic Substring
    小編和實驗室同學之前面試找工作,也只刷了劍指offer和這top 100算法題,在實際面試中也遇到了很多LeetCode上的原題。劍指offer算法最優解之前和大家分享了,LeetCode Top 100這100道算法題,每道題小編都刷了很多遍,並且總結了一種最適合面試時手撕算法的最優解法。後續每天和大家分享一道LeetCode top 100高頻算法題,以及小編總結的最優解法。
  • LeetCode刷題實戰3:最長不重複子串
    Given a string, find the length of the longest substring without repeating characters.https://leetcode.com/problems/longest-substring-without-repeating-characters/翻譯題目只有一句話:給定一個字符串,要求返回不包含重複字符的最長子串的長度。
  • LeetCode刷題筆記 - 3. 無重複字符的最長子串
    ,各個標籤的題目都有,可以整體練習,本公眾號後續會帶大家做一做上面的算法題。官方連結:https://leetcode-cn.com/problemset/all/一、題意難度:中等https://leetcode-cn.com/problems/longest-substring-without-repeating-characters
  • 一文帶你AC十道題【滑動窗口】
    從類型上說主要有:窗口大小不固定,求解最小的滿足條件的窗口(上面的 209 題就屬於這種)後面兩種我們統稱為可變窗口。當然不管是哪種類型基本的思路都是一樣的,不一樣的僅僅是代碼細節。無重複字符的最長子串): https://leetcode-cn.com/problems/longest-substring-without-repeating-characters/solution/pythonjavascript-hua-dong-chuang-kou-3-wu-zhong-fu/[2]76.
  • [每日一題]14. Longest Common Prefix
    Write a function to find the longest common prefix string amongst an array
  • Longest Common Subsequence 最長公共子序列
    A subsequence of a string is a new string generated from the original string with some characters(can be none) deleted without changing the relative order of the remaining characters.
  • 圖解LeetCode第 3 號問題:無重複字符的最長子串
    地址:https://github.com/MisterBooo/LeetCodeAnimationLeetCode上第 3 號問題:Longest Substring Without Repeating Characters。題目描述 給定一個字符串,找出不含有重複字符的最長子串的長度。
  • LeetCode刷題筆記(1)
    算法題,把解題的筆記放在這裡,希望跟各位讀者多多交流。值得注意的是,由於每個ListNode對象是單向傳遞的,所以需要在一開始保留一個根部的指針指向和的末位,計算完成後返回這個根部的指針。= divmod(v1+v2+carry, 10)            tmp.next = ListNode(v3)            tmp = tmp.next        return l3.next3.
  • 5 道經典 Python 算法題,地鐵上也能輕鬆學會(字符串篇)
    這 5 道題看似簡單,但做出來還真有點燒腦。答案也都很有營養,包含了不少 Python 的黑魔法,甚至會讓你驚呼:居然還有這種方法,一行代碼就能寫出來?!ChallengeO(n) time without extra memory.判斷字符串是否是回文,只需考慮字母數字字符,忽略大小寫。字符串的回文判斷問題,由於字符串可隨機訪問,故逐個比較首尾字符是否相等最為便利,即常見的『兩根指針』技法。此題忽略大小寫,並只考慮字母和數字字符。
  • Longest Common Prefix
    解法一 垂直比較Java思路:我們首先判定給定數組中長度最小的元素,然後按照這個min(length),將數組中元素依次進行比較,如果比較成功返回True,不成功則返回Fasle.public String longestCommonPrefix(String[] strs) {               if (strs.length==0) return "";        if (strs.length==1) return strs[0].substring(0);
  • 14種模式搞定面試算法編程題(PART I)
    好了不廢話啦,今天文章的主題就是分享14種解決面試算法編程題的思路(來自educative[1]),經過本人之前筆試面試經驗證明確實確實非常非常高頻,一定要十分熟悉。應用場景舉個慄子求根到葉子節點數字之和(LEETCODE)[19]從中序與後序遍歷序列構造二叉樹(LEETCODE)[21]7、Subset大量的編程面試問題涉及處理一組給定元素的排列和組合
  • 【面試錦囊】14種模式搞定面試算法編程題(1-7)
    好了不廢話啦,今天文章的主題就是分享14種解決面試算法編程題的思路(來自educative[1]),經過本人之前筆試面試經驗證明確實確實非常非常高頻,一定要十分熟悉。應用場景舉個慄子求根到葉子節點數字之和(LEETCODE)[19]從中序與後序遍歷序列構造二叉樹(LEETCODE)[21]7、Subset大量的編程面試問題涉及處理一組給定元素的排列和組合