LeetCode 30. 串聯全部單詞的子串 | Python

30. 串聯全部單詞的子串


題目來源:力扣(LeetCode)https://leetcode-cn.com/problems/substring-with-concatenation-of-all-wordspython

題目


給定一個字符串 s 和一些長度相同的單詞 words。找出 s 中剛好能夠由 words 中全部單詞串聯造成的子串的起始位置。bash

注意子串要與 words 中的單詞徹底匹配,中間不能有其餘字符,但不須要考慮 words 中單詞串聯的順序。微信

示例 1:app

輸入:
  s = "barfoothefoobarman",
  words = ["foo","bar"]
輸出:[0,9]
解釋:
從索引 0 和 9 開始的子串分別是 "barfoo" 和 "foobar" 。
輸出的順序不重要, [9,0] 也是有效答案。

示例 2:spa

輸入:
  s = "wordgoodgoodgoodbestword",
  words = ["word","good","best","word"]
輸出:[]

解題思路


思路:滑動窗口code

先用最簡單的思路去嘗試看可否解決問題。題目中說明要咱們找到子串與 words 的單詞徹底匹配。那麼最簡單的思路就是判斷子串是否符合,符合就將索引放到列表中,最後返回。blog

暴力解

具體的作法如上圖,循環遍歷索引,判斷子串是否符合。這裏主要的問題是如何判斷子串是否符合?索引

題目中有個提示【注意子串要與 words 中的單詞徹底匹配,中間不能有其餘字符,但不須要考慮 words 中單詞串聯的順序。】,也就是說題目中並不要求子串徹底按照順序,只須要對應個數相等且中間鏈接部分不會有其餘字符。這樣就算匹配。leetcode

這裏,咱們能夠藉助哈希表解決。一個哈希表存儲 words 的信息,鍵存儲的是單詞,值存儲的是單詞出現的個數。另一個哈希表一樣鍵存儲的是單詞,值存儲單詞出現的個數,這個哈希表在遍歷字符串的同時進行維護,而後與前面的哈希表中的值進行比對。這裏會出現以下狀況:rem

  • 當遍歷存儲的值大於存儲 words 的哈希表中的 value 值時,顯然是不成立的。進行判斷下一個子串
  • 當小於的時候,接着進行判斷。
  • 當子串徹底符合的狀況,那麼這就是要找的子串,將其索引存入列表中,最後返回。

但這裏其實沒必要移動 1 個字符去匹配。題目有說起【一些長度相同的單詞 words】,也就是說 words 中的單詞的長度都是同樣的。這樣,每次移動就能夠移動的偏移量就是單詞的長度。

那麼對單詞使用滑動窗口,步長就是單詞的長度 word_len。那麼在 0 ~ word_len 的範圍內,這裏每一個都做爲滑動窗口的起點,進行滑動,就能覆蓋全部字符串的組合。

下圖爲示例 1 以及使用滑動窗口的大體圖解:

示例 1

輸入:
  s = "barfoothefoobarman",
  words = ["foo","bar"]

輸出:[0,9]

滑動窗口

具體實現的代碼以下。

代碼實現


class Solution:
    def findSubstring(self, s: str, words: List[str]) -> List[int]:
        from collections import Counter

        # 處理特殊狀況
        if not s or not words:
            return []
        
        # 哈希表統計單詞出現次數,用之後續比較
        word_cnt = Counter(words)
        
        # 單詞長度
        word_len = len(words[0])

        # 返回結果列表
        ans = []

        # 遍歷,進行窗口滑動
        for i in range(word_len):
            left = i
            right = i
            cnt = 0

            # 哈希表記錄窗口的單詞出現次數
            window = Counter()

            # 限定邊界
            # 這裏表示窗口的內容不足以組成串聯全部單詞的子串,循環結束
            while left + len(words) * word_len <= len(s):
                # 窗口單詞出現的次數,與 word_cnt 對比
                while cnt <  len(words):
                    word = s[right:right+word_len]
                    # 若是單詞不在 words 中,或者此時單詞數量大於 words 中的單詞數量時,退出循環另外處理
                    # 單詞次數相等也跳出另外判斷
                    # 不然更新哈希表 window
                    if (word not in words) or (window[word] >= word_cnt[word]):
                        break
                    window[word] += 1
                    cnt += 1
                    right += word_len
                
                # 先判斷哈希表是否相等,相等則加入返回列表中
                if word_cnt == window:
                    ans.append(left)

                # 再處理單詞數溢出的狀況
                # 區分在於單詞是否在 words 中
                if word in words:
                    # 剔除左邊部分
                    left_word = s[left: left+word_len]
                    window[left_word] -= 1
                    left += word_len
                    cnt -= 1
                    
                else:
                    # 若是單詞不在 words 中,
                    # 清空哈希表,重置窗口開始位置
                    right += word_len
                    window.clear()
                    left = right
                    cnt = 0

        return ans

實現結果


實現結果

總結


  • 題意要求子串與 words 中單詞匹配,那麼就用最簡單的思路,遍歷字符串,判斷子串是否與 words 中的單詞是否匹配;
  • 主要的難題在於如何判斷是否匹配?題目中說起【中間不能有其餘字符,但不須要考慮 words 中單詞串聯的順序】,也就是說求得連續且單詞個數相等時,就能夠斷定爲匹配;
  • 綜合考慮,可使用滑動窗口來解決問題。又有題目中說明 words 中的單詞長度是一致的,那麼滑動窗口的步長就是單詞的長度 word_len。
  • 在 0 ~ word_len 的範圍內,每一個位置都做爲滑動窗口的起點,進行滑動,這樣就能覆蓋全部子串的組合。

文章原創,若是感受寫得還好,歡迎關注點贊。微信公衆號《書所集錄》同步更新,一樣歡迎關注。
相關文章
相關標籤/搜索