题目链接:https://leetcode-cn.com/problems/substring-with-concatenation-of-all-words/
题目描述
给定一个字符串 s 和一些长度相同的单词 words。找出 s 中恰好可以由 words 中所有单词串联形成的子串的起始位置。
注意子串要与 words 中的单词完全匹配,中间不能有其他字符,但不需要考虑 words 中单词串联的顺序。
示例 1:
输入:
s = "barfoothefoobarman",
words = ["foo","bar"]
输出:[0,9]
解释:
从索引 0 和 9 开始的子串分别是 "barfoor" 和 "foobar" 。
输出的顺序不重要, [9,0] 也是有效答案。
示例 2:
输入:
s = "wordgoodgoodgoodbestword",
words = ["word","good","best","word"]
输出:[]
思路
1 暴力+哈希
(1)用一个 unordered_map<string, int> need
存储words
中每个词的出现次数
(2)遍历字符串的每个字符位置,。从该位置起不断截取word
长度的子串,判断该word
是否出现在need
中。同时通过建立一个unordered_map<string, int> window
保存当前单词的出现次数。如果window
与need
的所有单词次数相同,则记录当前子串起始位置
外循环中每次移动1个字符,内循环每次移动一个word的长度
复杂度分析
时间复杂度:O(mn);其中m是s的长度,n是字典大小
空间复杂度:O(n)
class Solution {
public:
vector<int> findSubstring(string s, vector<string>& words) {
unordered_map<string, int> need;
vector<int> ret;
if(s.empty() || words.empty()) return ret;
int wordNum = words.size(), wordLen = words[0].size();
if (wordLen * wordNum > s.size()) return ret;
// 统计word次数
for(string s:words)
need[s] ++;
// 遍历所有子串 注意最后一个可能子串的起始坐标为s.size() - wordNum * wordLen
for (int i = 0; i <= s.size() - wordNum * wordLen ; i++) {
unordered_map<string, int> window;
int cnt = 0;
int begin = i;
int match = 0; // 匹配的词数目
while (cnt < wordNum){
string sub = s.substr(begin,wordLen);
cnt ++; // 当前子串的单词计数
begin += wordLen;
if(need.count(sub)){
window[sub] ++;
if(window[sub] == need[sub])
match ++;
else if(window[sub] > need[sub]){
match --;
break;
}
} else
break;
}
if(match == need.size())
ret.push_back(i);
}
return ret;
}
};
2 滑动窗口 O(n)
我们一直在 s 维护着所有单词长度总和的一个长度队列
复杂度分析
时间复杂度:O(m);其中m是s的长度,n是字典大小
空间复杂度:O(n)
class Solution {
public:
vector<int> findSubstring(string s, vector<string>& words) {
unordered_map<string, int> need;
vector<int> ret;
if(s.empty() || words.empty()) return ret;
int wordNum = words.size(), wordLen = words[0].size();
int allLen = wordNum * wordLen;
if (allLen > s.size()) return ret;
// 统计word次数
for(string s:words)
need[s] ++;
for (int i = 0; i < wordLen; ++i) {
int cnt = 0;
int left = i, right = i;
unordered_map<string, int> window;
while (right + wordLen <= s.size()){
string lastWord = s.substr(right,wordLen);
right += wordLen;
window[lastWord] ++;
cnt ++;
while (window[lastWord] > need[lastWord]){
string firstWord = s.substr(left,wordLen);
left += wordLen;
window[firstWord] --;
cnt --;
}
if(cnt == wordNum)
ret.push_back(left);
}
}
return ret;
}
};