题目描述
给定一个字符串 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] 由小写英文字母组成
算法分析
s中每次只移动1个字符;解法2改成每次移动一个单词的长度,此时s子串的所有可能结果有3种,所以外层循环小于一个单词的长度即可
i从0开始,每次移动一个单词的长度
i从1开始,每次移动一个单词的长度
i从2开始,每次移动一个单词的长度
右窗口每次移动一个单词的长度,获取该单词word;
如果该单词不在wordsMap中,说明该单词可以直接跳过,缩小窗口,左指针移动到右指针的位置;
如果该单词在wordsMap中,如果该单词在子串中的个数,比在wordsMap中的个数还多,说明需要缩小窗口,即左指针向右移动,一直到该单词在子串中的个数不比wordsMap中多为止
代码
class Solution {
public:
vector<int> findSubstring(string s, vector<string>& words) {
if(s.empty() || words.empty()) {
return std::vector<int>{};
}
int words_number = words.size();
int word_length = words[0].size();
unordered_map<std::string, int> needs;
for(std::string &word : words) {
++needs[word];
}
std::vector<int> res;
for(int i = 0; i < word_length; ++i){
int left = i, right = i;
int cur_words_number = 0;
unordered_map<std::string, int> windows;
while(right + word_length <= s.size()) {
std::string word = s.substr(right, word_length);
right += word_length;
if(needs[word] == 0) {
left = right;
windows.clear();
cur_words_number = 0;
} else {
++windows[word];
++cur_words_number;
while(windows[word] > needs[word]) {
std::string word_l = s.substr(left, word_length);
--windows[word_l];
--cur_words_number;
left += word_length;
}
if(cur_words_number == words_number) {
res.push_back(left);
}
}
}
}
return res;
}
};
时间复杂度分析
时间复杂度是 O(n),n是s的长度 空间复杂度是 O(m),m是words的单词数