【LeetCode】30.串聯所有單詞的子串
阿新 • • 發佈:2022-04-11
30.串聯所有單詞的子串
知識點:字串;滑動視窗;雜湊表
題目描述
給定一個字串 s 和一些 長度相同 的單詞 words 。找出 s 中恰好可以由 words 中所有單詞串聯形成的子串的起始位置。
注意子串要與 words 中的單詞完全匹配,中間不能有其他字元 ,但不需要考慮 words 中單詞串聯的順序。
示例
示例 1: 輸入:s = "barfoothefoobarman", words = ["foo","bar"] 輸出:[0,9] 解釋: 從索引 0 和 9 開始的子串分別是 "barfoo" 和 "foobar" 。 輸出的順序不重要, [9,0] 也是有效答案。 示例 2: 輸入:s = "wordgoodgoodgoodbestword", words = ["word","good","best","word"] 輸出:[] 示例 3: 輸入:s = "barfoofoobarthefoobarman", words = ["bar","foo","the"] 輸出:[6,9,12]
解法一:滑動視窗
這其實也是滑動視窗的一道典型題目
找到包含words中單詞的子串;要注意到題目中一個條件,words中的單詞都是長度相同的,子串其實就可以去想想滑動視窗。
當然也可以直接看word中的長度,然後每次擷取這麼長的子串,然後統計子串中的單詞和個數,形成第二個雜湊表,看和word中的雜湊表是否相同。這樣的話兩次for。
當然,我們可以用滑動視窗來優化這道題。
初始:統計words中每個字元的數量,形成hashmap1;
視窗的移動都是以單詞的長度為單位進行的, 沒必要一個字元一個字元的去移動;
1.right右移,試圖去找滿足條件的視窗
if當前的單詞不在hashmap1中,那前面肯定都不對了,left直接移動到right即可; if當前的單詞在hashmap1中,並且還沒有超過hashmap1中的個數,那right可以繼續移動 if當前的單詞在hashmap1中,但是當前視窗中包含的目前這個單詞個數已經超過了hashmap1中的個數,那就要把當前這個單詞對應的前面的部分都移出去,也就是左視窗要移動 所以怎麼能夠知道當前視窗內對應的單詞和個數呢,維持一個當前視窗的hashmap2,
2.left右移:
當現在的視窗內當前的單詞數量超過了需要的個數,那left要把這個單詞和其之前的單詞都移動出去(因為if left只移動一個,假設目前的這個單詞在後面重復,根本沒有意義,因為還是多著呢)。
除此之外,維持一個所需要的單詞總數量,這樣可以知道什麼時候滿足要求,向res中新增答案
- 其實仔細想一下這個過程,仍然是滿足滑動視窗的思想:毛毛蟲模型
右視窗移動,尋找可行解,然後會破壞掉條件(對應本題就是出現了沒有的單詞或者單詞出現的過多了)
然後右邊不動了,左視窗移動,讓整個視窗仍然滿足條件
與之前的難點就在於在其中穿插了兩個雜湊表,然後在其中判斷視窗是否會滿足最後答案的要求。
from collections import Counter, defaultdict
class Solution:
def findSubstring(self, s: str, words: List[str]) -> List[int]:
res = []
allwords_count = Counter(words) # 所有單詞的數量的雜湊表
word_count = len(words) # 單詞的數量
oneword_len = len(words[0]) #每個單詞的長度
for i in range(oneword_len): #前面不清楚具體是由哪個字元開始的
left = right = i
count = 0 #已經滿足的單詞數量
windowwords_count = defaultdict(int) # 統計視窗內每個單詞數量的雜湊表
while right + oneword_len <= len(s):
cur_word = s[right:right+oneword_len] #當前單詞
right += oneword_len
if cur_word not in allwords_count: #肯定匹配失敗
left = right #視窗右移
windowwords_count.clear() #字典清空
count = 0
else: #有這個單詞
windowwords_count[cur_word] += 1
count += 1
while windowwords_count.get(cur_word) > allwords_count.get(cur_word):
popword = s[left: left+oneword_len] #移出的單詞
windowwords_count[popword] -= 1
count -= 1
left += oneword_len #次數都減1,左視窗右移
if count == word_count:
res.append(left)
return res