这道题可以用滑动窗口的方式来解决,窗口的大小是words数组里面所有单词的总和。 因为字符串s如果有子串能完全匹配words,那肯定是words中的每个单词都要出现若干次的(words里面的单词可能有重复)。这样一来,我们就不用排列组合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" ] 输出:[]
考察知识点
字符串匹配、哈希表、双指针、字符串、滑动窗口
核心思想
方法一、先确定可能的组合形式,再一个一个的匹配。
思路简单,但是时间复杂度太大。
方法二、滑动窗口
这道题可以用滑动窗口的方式来解决,窗口的大小是words数组里面所有单词的总和。 因为字符串s如果有子串能完全匹配words,那肯定是words中的每个单词都要出现若干次的(words里面的单词可能有重复)。这样一来,我们就不用排列组合words中的一个一个字符串了。 以barfoothefoobarman为例,words=["foo","bar"],那么窗口大小就是6(foo+bar的长度)。
我们从字符串s的开始位置起,每次截取6位长度记做tmp,跟words的单词比较。words中的单词都放到一个map中,key是单词,value是这个单词出现的次数。我们比较tmp和map是否完全匹配,如果是就找到了一个索引位置。 我们按照这种方式迭代完整个数组,就可以求出解了。
代码概述:
1、几种特判情况,s为空,words为空。直接返回[]。
2、利用dict构造hash map,将每个单词以及出现的频率记录到字典中。
3、先获取单个word的长度,再计算所有word组成的字符串的长度。
4、开始循环,s中在最后all_words_size个字符,长度都不够all_words_size,不可能满足条件,所以循环终点在 len(s) - all_words_size + 1。
5、每次取tmp_str中长为all_words_size的子串,将子串和临时字典进行比较,如果是就将临时字典记录的频率-1,如果不在就跳出循环。当这个word记录频率为0时,删除该word。
6、当内层循环遍历完后,如果临时字典为空则表示全部匹配上了,则记录数组的下标。
7、循环结束。
参考连接
Python版本
方法一的实现
148 / 173 个通过测试用例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 class Solution : def strStrs (self, haystack: str , needle: str ) -> list: res = [] if len (haystack) == 0 : return res last_index = 0 is_first = True while haystack: tmp_index = self.strStr(haystack, needle) if tmp_index != -1 : if is_first: res.append(tmp_index) last_index = tmp_index is_first = False else : res.append(tmp_index + last_index + 1 ) last_index = tmp_index + last_index + 1 haystack = haystack[tmp_index+1 : ] else : return res return res def strStr (self, haystack: str , needle: str ) -> int: def calShiftMat (st ): dic = {} for i in range (len (st)-1 ,-1 ,-1 ): if not dic.get(st[i]): dic[st[i]] = len (st)-i dic["ot" ] = len (st)+1 return dic needle_len = len (needle) haystack_len = len (haystack) if needle_len > haystack_len:return -1 if needle=="" : return 0 dic = calShiftMat(needle) idx = 0 while idx+needle_len <= haystack_len: str_cut = haystack[idx:idx+needle_len] if str_cut==needle: return idx else : if idx+needle_len >= haystack_len: return -1 cur_c = haystack[idx+needle_len] if dic.get(cur_c): idx += dic[cur_c] else : idx += dic["ot" ] return -1 if idx+needle_len >= haystack_len else idx def permutations (self, n ): if n == 0 : return [] if n == 1 : return [[0 ]] res = [] indices = list (range (n)) res.append(tuple (indices)) cycles = list (range (n, 0 , -1 )) while n: for i in reversed (range (n)): cycles[i] -= 1 if cycles[i] == 0 : indices[i:] = indices[i+1 :] + indices[i:i+1 ] cycles[i] = n - i else : j = cycles[i] indices[i], indices[-j] = indices[-j], indices[i] res.append(tuple (indices)) break else : return res def findSubstring (self, s: str , words: list ) -> list: prs = self.permutations(len (words)) words_combinations = [] for permutation in prs: tmp_str = "" for i in permutation: tmp_str += words[i] words_combinations.append(tmp_str) words_combinations = set (words_combinations) res = [] for word in words_combinations: _index = self.strStrs(s, word) if len (_index): res += _index return res print("leet code accept!!!" ) Input = ["abababab" , "a" , "" , "foobarfoobar" , "barfoothefoobarman" , "wordgoodgoodgoodbestword" ] Input1 = [["a" ,"b" ], ["a" ], ["a" ], ["foo" ,"bar" ], ["foo" , "bar" ], ["word" ,"good" ,"best" ,"word" ]] Answer = [[0 ,1 ,2 ,3 ,4 ,5 ,6 ], [0 ], [], [0 , 3 , 6 ], [0 ,9 ], []]if __name__ == "__main__" : solution = Solution() for i in range (len (Input)): print("-" *50 ) result = solution.findSubstring(Input[i], Input1[i]) result.sort() print(result) print(Answer[i]) print(result==Answer[i])
方法二的实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 class Solution2 : def findSubstring (self, s: str , words: list ) -> list: if not s or len (s)==0 or not words or len (words)==0 : return [] words_map,res = dict (),[] for i in words: if i not in words_map: words_map[i] = 1 else : words_map[i] += 1 one_word_size = len (words[0 ]) all_words_size = len (words)*one_word_size s_len = len (s) for i in range (s_len-all_words_size+1 ): tmp_str, d = s[i:i+all_words_size], dict (words_map) for j in range (0 , len (tmp_str), one_word_size): key = tmp_str[j:j+one_word_size] if key in d: d[key] -= 1 if d[key]==0 : del d[key] else : break if not d: res.append(i) return res
时间复杂度\(O(N*M)\)
有效语法糖
1、注意Python中的赋值,可变对象(list、set、dict)和不可变对象(int、float、string)。
words_map = {"a" : 1 , "b" : 2 } d = words_map d = dict (words_map)