描述
该题来自于力扣第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]
提示:
* 1 <= s.length <= 104
* s 由小写英文字母组成
* 1 <= words.length <= 5000
* 1 <= words[i].length <= 30
* words[i]由小写英文字母组成
分析
需要知道words的一个组合是否是s的一个子串,由于words中的元素组合成的字符串是固定长度的,所以只需要考虑s中固定长度的子串,以实例2为例,words中每个词的长度为4,而words中元素个数也是4,所以组合成的字符串总长度为4*4 = 16
,所以遍历s的长度为16的子串,看看该子串里的元素是否和words一样。具体过程为:
wordgoodgoodgoodbestword ->
wordgoodgoodgoodbestword ->
wordgoodgoodgoodbestword -> ...
上述方法每次移动一个字符串,无法利用之前窗口的信息,显然不合理,由于每个元素长度为4,可以每次移动4个字符,即
*
以w
开始,长度为16的窗口,以4为步长移动:wordgoodgoodgoodbestword
-> wordgoodgoodgoodbestword ->
wordgoodgoodgoodbestword -> ...
*
以o
开始,长度为16的窗口,以4为步长移动:wordgoodgoodgoodbestword
-> wordgoodgoodgoodbestword
* 以r
开始以及以d
开始相应的窗口移动
优化后的方法同样可以遍历完所有子串,而且由于每次移出一个单词,移入一个单词,从而可以很好的维护窗口中的元素。
在模拟窗口移动时发现可以继续优化,比如即将移入的元素不存在于words中,表明直到将该元素移出为止,不可能存在满足条件的子串。另外一种可以优化的情况,如果即将移入的元素在words中,但是元素个数超过了words中的该元素个数呢?显然这时候需要移出该元素前面的所有元素(包含自身),这样才能让该元素的个数少一个。
考虑了上面的两种情况就可以保证子串里的元素都在words中,且每个元素的个数不超过words中的元素,所以该窗口必为words中的某个组合,因为如果某个元素的个数小与words中的该元素个数,那么窗口的长度必然小于16。
代码
python
1 | class Solution: |