本文目录
题目描述
给定一个字符串 s 和一个字符串数组 words。 words 中所有字符串 长度相同。
s 中的 串联子串 是指一个包含 words 中所有字符串以任意顺序排列连接起来的子串。
例如,如果 words = ["ab","cd","ef"], 那么 "abcdef", "abefcd","cdabef", "cdefab","efabcd", 和 "efcdab" 都是串联子串。 "acdbef" 不是串联子串,因为他不是任何 words 排列的连接。
返回所有串联子串在 s 中的开始索引。你可以以 任意顺序 返回答案。
示例 1:
输入:s = "barfoothefoobarman", words = ["foo","bar"] 输出:[0,9] 解释:因为 words.length == 2 同时 words[i].length == 3,连接的子字符串的长度必须为 6 子串 "barfoo" 开始位置是 0。它是 words 中以 ["bar","foo"] 顺序排列的连接 子串 "foobar" 开始位置是 9。它是 words 中以 ["foo","bar"] 顺序排列的连接 输出顺序无关紧要。返回 [9,0] 也是可以的
示例 2:
输入:s = "wordgoodgoodgoodbestword", words = ["word","good","best","word"] 输出:[] 解释:因为 words.length == 4 并且 words[i].length == 4,所以串联子串的长度必须为 16 s 中没有子串长度为 16 并且等于 words 的任何顺序排列的连接 所以我们返回一个空数组
示例 3:
输入:s = "barfoofoobarthefoobarman", words = ["bar","foo","the"] 输出:[6,9,12] 解释:因为 words.length == 3 并且 words[i].length == 3,所以串联子串的长度必须为 9 子串 "foobarthe" 开始位置是 6。它是 words 中以 ["foo","bar","the"] 顺序排列的连接 子串 "barthefoo" 开始位置是 9。它是 words 中以 ["bar","the","foo"] 顺序排列的连接 子串 "thefoobar" 开始位置是 12。它是 words 中以 ["the","foo","bar"] 顺序排列的连接
个人C++解答
GYL
内存爆炸:
考虑采用两个哈希表,滑动窗口一个,原words一个,每个窗口建立一个表,判断是否与原表相同,相同的话该子串有效,否则无效;
class Solution {
public:
vector<int> findSubstring(string s, vector<string>& words) {
std::unordered_map<string, int> myMap;
for (auto word : words) myMap[word] += 1;
vector<int> result;
int word_length = words[0].size();
int word_num = words.size();
if (s.size() < word_length * words.size())
return result;
int left = 0, right = word_length * word_num;
std::unordered_map<string, int> myTemp;
for (; right <= s.size(); left++, right++) {
myTemp.clear();
for (int t_l = 0; t_l < word_num; t_l++) {
string temp_key = s.substr(left, right).substr(word_length * t_l, word_length);
myTemp[temp_key] += 1;
}
if(areMapsEqual(myTemp,myMap)) result.push_back(left);
}
return result;
}
bool areMapsEqual(const std::unordered_map<string, int>& map1,
const std::unordered_map<string, int>& map2) {
if (map1.size() != map2.size()) {
return false;
}
for (const auto& pair : map1) {
auto it = map2.find(pair.first);
if (it == map2.end() || it->second != pair.second) {
return false;
}
}
return true;
}
};
GYL
内存爆炸:
依次滑动单个窗口,拆分字符串,传入HashMap,遍历words,每个HashMap[word]-=1,判断HashMap的大小是否为0,为0的话说明拆分成的子串每个都在words中存在过且数量对应;
class Solution {
public:
vector<int> findSubstring(string s, vector<string>& words) {
unordered_map<string, int> myMap;
vector<int> result;
int word_length = words[0].size();
int word_num = words.size();
if (s.size() < word_length * words.size())
return result;
int left = 0, right = word_length * word_num,flag = 0;
for (; right <= s.size(); left++, right++) {
flag = 0;
myMap.clear();
for (int t_l = 0; t_l < word_num; t_l++) {
string temp_key = s.substr(left, right).substr(word_length * t_l, word_length);
myMap[temp_key] ++;
}
for(auto word:words){
myMap[word]--;
if(myMap[word]==0){
myMap.erase(word);
}
}
if(myMap.size()==0)
result.push_back(left);
}
return result;
}
};
GYL
内存允许,时间爆炸:
优化代码,使得裁剪字符串和构建哈希表的次数变少,从而减少内存开销,时间复杂度没变
class Solution {
public:
vector<int> findSubstring(string s, vector<string>& words) {
vector<int> result;
if (s.empty() || words.empty()) return result;
if (s.size() < words[0].size() * words.size()) return result;
int wordLen = words[0].size();
int wordNum = words.size();
unordered_map<string, int> wordCount;
for (const string& word : words) {
wordCount[word]++;
}
int totalLen = wordLen * wordNum;
for (int left = 0; left <= s.size() - totalLen; left++) {
unordered_map<string, int> matched(wordCount);
for (int right = left; right < left + totalLen; right += wordLen) {
string word = s.substr(right, wordLen);
if (matched.find(word) == matched.end()) break;
matched[word]--;
if (matched[word] == 0) {
matched.erase(word);
}
}
if (matched.empty()) {
result.push_back(left);
}
}
return result;
}
};
GPT优化代码
核心优化:for (int i = 0; i < wordLen; i++) { unordered_map<string, int> window; int left = i, right = i, count = 0;从每个可能的起点(0 到 wordLen - 1)开始,确保所有可能的起点都能被覆盖。
GPT
时间复杂度降低到了 O(n * wordLen), 其中 n 是字符串 s 的长度,wordLen 是单词的长度
class Solution {
public:
vector<int> findSubstring(string s, vector<string>& words) {
vector<int> result;
if (s.empty() || words.empty()) return result;
int wordLen = words[0].size();
int wordNum = words.size();
int totalLen = wordLen * wordNum;
if (s.size() < totalLen) return result;
unordered_map<string, int> wordCount;
for (const string& word : words) {
wordCount[word]++;
}
// Iterate over each possible starting index within the first word length
for (int i = 0; i < wordLen; i++) {
unordered_map<string, int> window;
int left = i, right = i, count = 0;
while (right + wordLen <= s.size()) {
string word = s.substr(right, wordLen);
right += wordLen;
if (wordCount.find(word) != wordCount.end()) {
window[word]++;
count++;
// If the word count exceeds, slide the window from the left
while (window[word] > wordCount[word]) {
string leftWord = s.substr(left, wordLen);
window[leftWord]--;
left += wordLen;
count--;
}
// If count equals wordNum, a valid substring is found
if (count == wordNum) {
result.push_back(left);
}
} else {
// Reset the window
window.clear();
count = 0;
left = right;
}
}
}
return result;
}
};

