原题目
代码分析
方法一:暴力法
图片来源点击此处
i从0开始遍历,在所有单词总长范围内,判断是否符合要求,符合要求添加当前i下标
i移向下一个,继续判断
要找到满足所有words数组单词的子串
1.先统计words每个单词的个数
2.从i开始遍历,截取单个单词长度的字符串,如果该字符串比 words的该单词个数小,则i移向下一个单词的首地址,否则停止,如果能遍历到words所有单词总长度结尾,即符合条件
class Solution {
public:
vector<int> findSubstring(string s, vector<string>& words) {
if(words.size()==0)return {};
vector<int>res;
unordered_map<string ,int>m1,m2;
for(auto word :words){//统计words各个单词个数
m1[word]++;
}
int len = words.size()*words[0].size(); //words所有单词总长度
for(int i = 0; i+len <= s.size(); i++){//从0开始遍历
int j = i;
for(; j < i+len; j+=words[0].size()){//每次判断长度为len的字符串是否包含所有的words
string temp = s.substr(j,words[0].size());//截取单个单词
if(m2[temp]>=m1[temp]){//如果比words的该单词多,说明不符,停止
break;
}
m2[temp]++;//统计s各个截取单词数
}
if(j == i+len){//如果遍历到最后,符合要求
res.push_back(i);
}
m2.clear();//清空s中统计的单词,方便下一次重新开始统计
}
return res;
}
};
方法二: 双指针+滑动窗口
因为words的单个单词等长,设单词长度为n,所以可以分为n类情况
对每种情况进行相同操作:
1.左右指针均从当前情况的下标i开始
2.如果右指针的当前截取单词不在words中,说明当前范围不符合条件,直接将左右指针移向下一个单词,重新统计截取的单词
3.如果右指针当前所截取的单词比words的还多,说明当前范围不符合条件,缩减左边界,并去掉统计过的单词,直到右指针当前所截取的单词比words少
4.如果右指针当前所截取的单词比words少,扩展右边界,直到满足words所有单词为止,即 右指针和左指针的区间长度为words所有单词总长度,符合条件,左指针为一个答案
5.以上滑动窗口执行n次
class Solution {
public:
vector<int> findSubstring(string s, vector<string>& words) {
if(words.size()==0)return {};
vector<int>res;
unordered_map<string ,int>m1,m2;//m1统计words各个单词数,m2统计截取的各个单词数
for(auto word :words){
m1[word]++;
}
int len = words.size()*words[0].size();
int word_size = words[0].size();
for(int i = 0; i < word_size&&i+len <= s.size(); i++){//1~word_size种情况
int j = i, k = i;
while(k<s.size()){
string temp = s.substr(k,word_size);
if(m1[temp] == 0){//如果words不存在该单词,直接从下一个开始重写统计
k+=word_size;
j = k;
m2.clear();
}else if(m2[temp]<m1[temp]){//如果比words该单词少,继续扩展右边界
m2[temp]++;
k+=word_size;
}else{//如果比words该单词多,缩减左边界,直至比words该单词少
m2[s.substr(j,word_size)]--;
j+=word_size;
}
if(k-j==len){//如果找到words所有单词,添加左指针
res.push_back(j);
}
}
m2.clear();
}
return res;
}
};