快轉到主要內容
  1. LeetCode/

LeetCode 30: Substring with Concatenation of All Words

·2 分鐘· ·
LeetCode Daily Hard String Sliding Window Two Pointers Hash Map Frequency-Counter Brute-Force
Wei Yi Chung
作者
Wei Yi Chung
Working at the contributing of open source, distributed systems, and data engineering.
目錄

基本資料
#

難易度:困難 第一次嘗試:2025-08-09

  • 總花費時間:00:00.00

解題思路
#

先從暴力法出發:針對每個起點逐一消耗單字,若全部吻合就記錄起點,但容易 TLE。 接著做計數優化,減少重複切片與狀態重建,雖有改善但本質仍是暴力。 最後利用所有單字長度相同的特性,將字串視為長度 word_len 的區塊,對 offset ∈ [0, word_len) 做滑動視窗,搭配頻率表在失衡時從左邊彈出回復有效視窗。

解法
#

作法一 — 暴力
#

class Solution:
    def findSubstring(self, s: str, words: List[str]) -> List[int]:
        if not s or not words:
            return []

        m = len(s)
        n = len(words[0])
        total_words = len(words)
        total_len = n * total_words
        if m < total_len:
            return []

        target = Counter(words)
        res: List[int] = []

        for i in range(m - total_len + 1):
            left = i
            remaining = total_words
            need = target.copy()

            while remaining > 0:
                word = s[left:left + n]
                if need[word] > 0:
                    need[word] -= 1
                    left += n
                    remaining -= 1
                else:
                    break

            if remaining == 0:
                res.append(i)

        return res

作法二 — 計數優化(每個起點及早終止)
#

class Solution:
    def findSubstring(self, s: str, words: List[str]) -> List[int]:
        if not s or not words:
            return []

        m = len(s)
        n = len(words[0])
        total_words = len(words)
        total_len = n * total_words
        if m < total_len:
            return []

        target = Counter(words)
        res: List[int] = []

        for i in range(m - total_len + 1):
            left = i
            seen = defaultdict(int)
            matched = 0

            for right in range(left, m - n + 1, n):
                word = s[right:right + n]
                if word in target and seen[word] < target[word]:
                    seen[word] += 1
                    matched += 1
                else:
                    break

            if matched == total_words:
                res.append(left)

        return res

作法三 — 偏移量滑動視窗(最優)
#

class Solution:
    def findSubstring(self, s: str, words: List[str]) -> List[int]:
        res = []
        word_len = len(words[0])
        n = len(words)
        word_count = Counter(words)

        for i in range(word_len):
            word_freq = defaultdict(int)
            count = 0
            start = i

            for j in range(i, len(s) - word_len + 1, word_len):
                word = s[j: j + word_len]

                if word not in word_count:
                    count = 0
                    start = j + word_len
                    word_freq = defaultdict(int)
                    continue

                count += 1
                word_freq[word] += 1

                while word_freq[word] > word_count[word]:
                    word_freq[s[start: start + word_len]] -= 1
                    count -= 1
                    start += word_len

                if count == n:
                    res.append(start)

        return res

複雜度
#

  • 作法一(暴力):O((|s| - total_len + 1) × num_words) 時間,O(k) 空間
  • 作法二(計數優化):O((|s| - total_len + 1) × num_words) 時間(常數較佳),O(k) 空間
  • 作法三(滑動視窗):O(|s|) 時間,O(k) 空間;k 為 words 中不同單字個數

收穫
#

  • 單字長度一致可用 word_len 為步長做偏移量滑動視窗,涵蓋所有起點。
  • 無效單字時重置視窗,有效避免二次方級別回溯。
  • 以頻率控制的滑動視窗避免從頭重檢,實務上最穩定。

遇到的問題
#

相關文章

LeetCode 1695: Maximum Erasure Value (Sliding Window)
·1 分鐘
LeetCode Daily Medium Sliding Window Two Pointers Array Hashset
LeetCode 解題紀錄
LeetCode 209: Minimum Size Subarray Sum
·1 分鐘
LeetCode Daily Medium Array Sliding Window Two Pointers Prefix Sum Binary-Search
滑動視窗與雙指針,最小化子陣列長度(總和 ≥ target)。
LeetCode 3439: Reschedule Meetings for Maximum Free Time (Sliding Window)
·2 分鐘
LeetCode Daily Medium Sliding Window Two Pointers Intervals Array
LeetCode 解題紀錄
LeetCode 76: Minimum Window Substring
·1 分鐘
LeetCode Daily Hard Sliding Window String Hash Map Two Pointers Variable Window
LeetCode 解題紀錄 - 可變大小滑動窗口與字符計數
LeetCode 1456: Maximum Number of Vowels in a Substring of Given Length
·1 分鐘
LeetCode Daily Medium Sliding Window String Vowel Counting Fixed Window
LeetCode 解題紀錄 - 固定大小滑動窗口與元音計數
LeetCode 239: Sliding Window Maximum
·1 分鐘
LeetCode Daily Hard Monotonic Queue
LeetCode 解題紀錄