30.串联所有单词的子串 知识点:字符串;滑动窗口;哈希表 题目描述 给定一个字符串 s 和一些 长度相同 的单词 words 。找出 s 中恰好可以由 words 中所有单词串联形成的子串的起始位置
题目描述知识点:字符串;滑动窗口;哈希表
给定一个字符串 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