【LeetCode】30.串联所有单词的子串

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
posted @ 2022-04-11 15:46  Curryxin  阅读(77)  评论(0编辑  收藏  举报
Live2D